Base64 Encoding & Decoding in Rust
Rust's standard library does not include Base64. The base64 crate is the de facto standard — it provides a flexible Engine-based API that supports standard, URL-safe, and custom alphabets.
Adding the Dependency
# Cargo.toml [dependencies] base64 = "0.22"
Basic Encode and Decode
use base64::{Engine as _, engine::general_purpose};
fn main() {
let input = b"Hello, world!";
// Encode to String
let encoded = general_purpose::STANDARD.encode(input);
println!("{}", encoded); // "SGVsbG8sIHdvcmxkIQ=="
// Decode to Vec<u8>
let decoded = general_purpose::STANDARD.decode(&encoded).unwrap();
println!("{}", String::from_utf8(decoded).unwrap()); // "Hello, world!"
}
Available Engines
use base64::engine::general_purpose; // Standard Base64 with padding general_purpose::STANDARD // + / with = // Standard Base64 without padding general_purpose::STANDARD_NO_PAD // + / without = // URL-safe with padding general_purpose::URL_SAFE // - _ with = // URL-safe without padding (most common for JWTs) general_purpose::URL_SAFE_NO_PAD // - _ without =
URL-Safe Base64
use base64::{Engine as _, engine::general_purpose};
let data = b"data for a URL parameter";
// Encode URL-safe without padding
let encoded = general_purpose::URL_SAFE_NO_PAD.encode(data);
println!("{}", encoded); // No + / or = characters
// Decode
let decoded = general_purpose::URL_SAFE_NO_PAD.decode(&encoded).unwrap();
assert_eq!(decoded, data);
Encoding to a Pre-allocated Buffer
For performance-critical code, encode directly into a buffer to avoid allocation:
use base64::{Engine as _, engine::general_purpose};
let input = b"Hello, world!";
// Calculate output size: ceil(input.len() / 3) * 4
let encoded_len = base64::encoded_len(input.len(), true).unwrap();
let mut buf = vec![0u8; encoded_len];
let written = general_purpose::STANDARD
.encode_slice(input, &mut buf)
.unwrap();
let encoded = std::str::from_utf8(&buf[..written]).unwrap();
println!("{}", encoded);
Encoding Files
use base64::{Engine as _, engine::general_purpose};
use std::fs;
fn encode_file(path: &str) -> Result<String, Box<dyn std::error::Error>> {
let bytes = fs::read(path)?;
Ok(general_purpose::STANDARD.encode(&bytes))
}
fn decode_to_file(encoded: &str, path: &str) -> Result<(), Box<dyn std::error::Error>> {
let bytes = general_purpose::STANDARD.decode(encoded)?;
fs::write(path, bytes)?;
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let encoded = encode_file("image.png")?;
decode_to_file(&encoded, "output.png")?;
Ok(())
}
Error Handling
use base64::{Engine as _, engine::general_purpose, DecodeError};
fn safe_decode(s: &str) -> Result<Vec<u8>, DecodeError> {
// Try standard first, then URL-safe
general_purpose::STANDARD
.decode(s)
.or_else(|_| general_purpose::URL_SAFE_NO_PAD.decode(s))
}
match safe_decode("SGVsbG8=") {
Ok(bytes) => println!("{}", String::from_utf8_lossy(&bytes)),
Err(e) => eprintln!("Decode error: {}", e),
}
Custom Engine
For exotic use cases (custom alphabet, non-standard padding), you can build a custom engine:
use base64::engine::{GeneralPurpose, GeneralPurposeConfig};
use base64::alphabet;
let custom_engine = GeneralPurpose::new(
&alphabet::URL_SAFE,
GeneralPurposeConfig::new()
.with_decode_padding_mode(base64::engine::DecodePaddingMode::Indifferent)
);
let encoded = custom_engine.encode(b"data");
Debug your Rust Base64 output
Paste the encoded string from your Rust program into base64.dev to inspect and decode it.
Open base64.dev →