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 →