Loading page…
Rust walkthroughs
Loading page…
The hex crate provides utilities for encoding binary data to hexadecimal strings and decoding hex strings back to binary. It's a simple, zero-dependency crate that handles the common task of representing bytes as hex characters. This is essential for cryptographic hashes, binary protocols, debugging binary data, and any situation where you need a human-readable representation of raw bytes. The crate offers both a simple functional API and a trait-based approach for more ergonomic usage.
Key concepts:
# Cargo.toml
[dependencies]
hex = "0.4"use hex::{encode, decode};
fn main() {
let bytes = b"hello";
let hex_string = encode(bytes);
println!("Hex: {}", hex_string); // "68656c6c6f"
let decoded = decode(&hex_string).unwrap();
println!("Decoded: {:?}", decoded); // [104, 101, 108, 108, 111]
}use hex::{encode, encode_upper};
fn main() {
// Encode bytes to lowercase hex
let bytes = b"Hello, World!";
let hex_lower = encode(bytes);
println!("Lowercase: {}", hex_lower);
// Encode bytes to uppercase hex
let hex_upper = encode_upper(bytes);
println!("Uppercase: {}", hex_upper);
// Encode a slice
let data = [0x00, 0xff, 0xab, 0xcd];
println!("Hex: {}", encode(data));
// Encode Vec<u8>
let vec = vec![0xde, 0xad, 0xbe, 0xef];
println!("Hex: {}", encode(vec));
}use hex::decode;
fn main() {
// Decode hex string to bytes
let hex = "48656c6c6f";
let bytes = decode(hex).unwrap();
println!("Bytes: {:?}", bytes);
println!("As string: {}", String::from_utf8_lossy(&bytes));
// Case-insensitive decoding
let mixed_case = "DeadBEEF";
let bytes = decode(mixed_case).unwrap();
println!("Decoded: {:x?}", bytes);
// With or without 0x prefix
let with_prefix = "0x1234";
let without_prefix = "1234";
// Note: hex crate doesn't handle 0x prefix by default
// Strip it manually if needed
let stripped = with_prefix.strip_prefix("0x").unwrap_or(with_prefix);
let bytes = decode(stripped).unwrap();
println!("Stripped and decoded: {:?}", bytes);
}use hex::{decode, FromHexError};
fn main() {
// Invalid hex characters
match decode("hello world") {
Ok(bytes) => println!("Decoded: {:?}", bytes),
Err(FromHexError::InvalidHexCharacter { c, index }) => {
println!("Invalid character '{}' at index {}", c, index);
}
Err(e) => println!("Error: {}", e),
}
// Odd length hex string
match decode("abc") {
Ok(bytes) => println!("Decoded: {:?}", bytes),
Err(FromHexError::OddLength) => {
println!("Hex string must have even length");
}
Err(e) => println!("Error: {}", e),
}
// Safe decode function
fn safe_decode(hex: &str) -> Result<Vec<u8>, String> {
decode(hex).map_err(|e| format!("Hex decode error: {}", e))
}
match safe_decode("invalid") {
Ok(bytes) => println!("Success: {:?}", bytes),
Err(e) => println!("Error: {}", e),
}
}use hex::FromHex;
fn main() {
// Decode into Vec<u8>
let bytes = Vec::from_hex("48656c6c6f").unwrap();
println!("Vec: {:?}", bytes);
// Decode into fixed-size array
let array: [u8; 4] = <[u8; 4]>::from_hex("deadbeef").unwrap();
println!("Array: {:x?}", array);
// Works with any type that implements FromHex
fn decode_to<T: FromHex>(hex: &str) -> Result<T, T::Error> {
T::from_hex(hex)
}
let vec: Vec<u8> = decode_to("cafebabe").unwrap();
println!("Decoded: {:x?}", vec);
}use hex::ToHex;
fn main() {
let bytes = b"hello";
// Encode to String
let hex_string = bytes.encode_hex::<String>();
println!("Hex: {}", hex_string);
// Encode to uppercase
let hex_upper = bytes.encode_hex_upper::<String>();
println!("Upper: {}", hex_upper);
// Write to existing buffer
let mut buffer = String::new();
bytes.write_hex(&mut buffer).unwrap();
println!("Buffer: {}", buffer);
}use hex::{encode, decode};
use std::collections::HashMap;
// Simulated hash storage
struct HashStore {
hashes: HashMap<String, Vec<u8>>,
}
impl HashStore {
fn new() -> Self {
Self { hashes: HashMap::new() }
}
fn store(&mut self, key: &str, hash: &[u8]) {
self.hashes.insert(key.to_string(), hash.to_vec());
}
fn get_hex(&self, key: &str) -> Option<String> {
self.hashes.get(key).map(|bytes| encode(bytes))
}
fn verify(&self, key: &str, hex_hash: &str) -> bool {
match decode(hex_hash) {
Ok(provided) => {
self.hashes.get(key)
.map(|stored| stored == &provided)
.unwrap_or(false)
}
Err(_) => false,
}
}
}
fn main() {
let mut store = HashStore::new();
// Store a "hash" (simulated SHA-256 would be 32 bytes)
let hash = [0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0];
store.store("file.txt", &hash);
// Get as hex
println!("Stored hash: {}", store.get_hex("file.txt").unwrap());
// Verify
println!("Valid: {}", store.verify("file.txt", "123456789abcdef0"));
println!("Invalid: {}", store.verify("file.txt", "aabbccdd"));
}use hex::{encode, decode};
fn main() {
// Simulated binary packet
let packet: Vec<u8> = vec![
0x7e, // Start byte
0x01, // Version
0x00, 0x10, // Length (16)
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21, // Payload
0x7e, // End byte
];
// Print packet as hex for debugging
println!("Packet: {}", encode(&packet));
// Format nicely
fn format_hex_dump(data: &[u8], bytes_per_line: usize) {
for (i, chunk) in data.chunks(bytes_per_line).enumerate() {
let hex: Vec<String> = chunk.iter().map(|b| format!("{:02x}", b)).collect();
let ascii: String = chunk.iter()
.map(|b| if b.is_ascii_graphic() { *b as char } else { '.' })
.collect();
println!("{:08x} {:<width$} {}",
i * bytes_per_line,
hex.join(" "),
ascii,
width = bytes_per_line * 3 - 1
);
}
}
println!("\nHex dump:");
format_hex_dump(&packet, 8);
}use hex::{encode, decode};
use std::collections::HashMap;
fn parse_hex_config(config: &HashMap<&str, &str>) -> Result<HashMap<String, Vec<u8>>, String> {
let mut result = HashMap::new();
for (key, hex_value) in config {
let bytes = decode(hex_value)
.map_err(|e| format!("Failed to decode '{}' for key '{}': {}", hex_value, key, e))?;
result.insert(key.to_string(), bytes);
}
Ok(result)
}
fn main() {
let mut config = HashMap::new();
config.insert("api_key", "deadbeef12345678");
config.insert("secret", "cafebabedeadbeef");
match parse_hex_config(&config) {
Ok(parsed) => {
for (key, value) in &parsed {
println!("{}: {}", key, encode(value));
}
}
Err(e) => println!("Error: {}", e),
}
}use hex::{encode, decode};
struct Color {
r: u8,
g: u8,
b: u8,
}
impl Color {
fn from_hex(hex: &str) -> Result<Self, String> {
let clean = hex.strip_prefix('#').unwrap_or(hex);
if clean.len() != 6 {
return Err("Hex color must be 6 characters".to_string());
}
let bytes = decode(clean)
.map_err(|e| format!("Invalid hex: {}", e))?;
Ok(Self {
r: bytes[0],
g: bytes[1],
b: bytes[2],
})
}
fn to_hex(&self) -> String {
encode([self.r, self.g, self.b])
}
fn to_hex_pretty(&self) -> String {
format!("#{}", self.to_hex())
}
}
fn main() {
let red = Color::from_hex("ff0000").unwrap();
println!("Red: rgb({}, {}, {})", red.r, red.g, red.b);
println!("Back to hex: {}", red.to_hex_pretty());
let blue = Color::from_hex("#0000ff").unwrap();
println!("Blue: {}", blue.to_hex_pretty());
}use hex::{encode, decode};
struct MacAddress([u8; 6]);
impl MacAddress {
fn from_hex(hex: &str) -> Result<Self, String> {
let clean: String = hex.chars().filter(|c| *c != ':' && *c != '-').collect();
if clean.len() != 12 {
return Err("MAC address must have 12 hex digits".to_string());
}
let bytes = decode(&clean)
.map_err(|e| format!("Invalid hex: {}", e))?;
let mut arr = [0u8; 6];
arr.copy_from_slice(&bytes);
Ok(Self(arr))
}
fn to_hex(&self) -> String {
encode(self.0)
}
fn to_colon_hex(&self) -> String {
format!("{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
self.0[0], self.0[1], self.0[2],
self.0[3], self.0[4], self.0[5])
}
fn to_dash_hex(&self) -> String {
format!("{:02X}-{:02X}-{:02X}-{:02X}-{:02X}-{:02X}",
self.0[0], self.0[1], self.0[2],
self.0[3], self.0[4], self.0[5])
}
}
fn main() {
let mac = MacAddress::from_hex("aa:bb:cc:dd:ee:ff").unwrap();
println!("Plain: {}", mac.to_hex());
println!("Colon: {}", mac.to_colon_hex());
println!("Dash: {}", mac.to_dash_hex());
}use hex::{encode, decode};
struct Uuid([u8; 16]);
impl Uuid {
fn from_hex(hex: &str) -> Result<Self, String> {
let clean: String = hex.chars().filter(|c| *c != '-').collect();
if clean.len() != 32 {
return Err("UUID must have 32 hex digits".to_string());
}
let bytes = decode(&clean)
.map_err(|e| format!("Invalid hex: {}", e))?;
let mut arr = [0u8; 16];
arr.copy_from_slice(&bytes);
Ok(Self(arr))
}
fn to_hex(&self) -> String {
encode(self.0)
}
fn to_hyphenated(&self) -> String {
let h = self.to_hex();
format!("{}-{}-{}-{}-{}",
&h[0..8], &h[8..12], &h[12..16], &h[16..20], &h[20..32])
}
}
fn main() {
let uuid = Uuid::from_hex("550e8400-e29b-41d4-a716-446655440000").unwrap();
println!("Plain: {}", uuid.to_hex());
println!("Hyphenated: {}", uuid.to_hyphenated());
}use hex::encode;
fn hex_view(data: &[u8]) {
println!("Offset 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ASCII");
println!("-------- -------------------------------- ----------------");
for (offset, chunk) in data.chunks(16).enumerate() {
let mut hex_part = String::new();
let mut ascii_part = String::new();
for (i, byte) in chunk.iter().enumerate() {
if i == 8 {
hex_part.push(' ');
}
hex_part.push_str(&format!("{:02x} ", byte));
if byte.is_ascii_graphic() {
ascii_part.push(*byte as char);
} else {
ascii_part.push('.');
}
}
// Pad if chunk is less than 16 bytes
for i in chunk.len()..16 {
if i == 8 {
hex_part.push(' ');
}
hex_part.push_str(" ");
}
println!("{:08x} {} {}", offset * 16, hex_part, ascii_part);
}
}
fn main() {
let data = b"Hello, World! This is some binary data with special bytes like \x00\xff\xab.";
hex_view(data);
}use hex::{encode, decode};
fn compare_hex(a: &str, b: &str) -> Result<bool, String> {
let bytes_a = decode(a).map_err(|e| format!("Decode A: {}", e))?;
let bytes_b = decode(b).map_err(|e| format!("Decode B: {}", e))?;
Ok(bytes_a == bytes_b)
}
// Case-insensitive comparison
fn compare_hex_nocase(a: &str, b: &str) -> Result<bool, String> {
Ok(a.to_lowercase() == b.to_lowercase())
}
fn main() {
println!("Equal: {}", compare_hex("ABCDEF", "abcdef").unwrap());
println!("Equal: {}", compare_hex("123456", "123457").unwrap());
}use hex::encode;
fn main() {
// For large data, encode in chunks
let large_data: Vec<u8> = (0..255).collect();
// Process in chunks to avoid large allocations
let mut hex_string = String::with_capacity(large_data.len() * 2);
for chunk in large_data.chunks(32) {
hex_string.push_str(&encode(chunk));
}
println!("Hex length: {}", hex_string.len());
println!("First 20 chars: {}", &hex_string[..20]);
}use hex::encode;
fn main() {
// Quick reference table
println!("Decimal Hex Binary");
println!("------- ----- ------");
for i in 0u8..=15 {
let hex = encode(&[i]);
println!("{:7} {:5} {:06b}", i, hex, i);
}
}use hex::decode;
fn parse_hex_literal(s: &str) -> Result<Vec<u8>, String> {
let clean = s
.strip_prefix("0x")
.or_else(|| s.strip_prefix("0X"))
.unwrap_or(s);
decode(clean).map_err(|e| format!("Invalid hex literal: {}", e))
}
fn main() {
let with_prefix = parse_hex_literal("0xdeadbeef").unwrap();
println!("Parsed: {:x?}", with_prefix);
let without_prefix = parse_hex_literal("cafebabe").unwrap();
println!("Parsed: {:x?}", without_prefix);
}use hex::encode;
#[derive(Debug)]
#[repr(C)]
struct PacketHeader {
magic: u32,
version: u16,
flags: u16,
}
fn main() {
let header = PacketHeader {
magic: 0x12345678,
version: 0x0100,
flags: 0x0001,
};
// Convert to bytes (be careful with endianness and padding!)
let bytes: [u8; std::mem::size_of::<PacketHeader>()] = unsafe {
std::mem::transmute(header)
};
println!("Header as hex: {}", encode(bytes));
println!("Size: {} bytes", std::mem::size_of::<PacketHeader>());
}# Cargo.toml
[dependencies]
hex = { version = "0.4", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct KeyPair {
#[serde(with = "hex")]
public_key: Vec<u8>,
#[serde(with = "hex")]
private_key: Vec<u8>,
}
fn main() {
let keypair = KeyPair {
public_key: vec![0x02, 0x03, 0x04, 0x05],
private_key: vec![0xde, 0xad, 0xbe, 0xef],
};
let json = serde_json::to_string(&keypair).unwrap();
println!("JSON: {}", json);
let parsed: KeyPair = serde_json::from_str(&json).unwrap();
println!("Parsed: {:?}", parsed);
}encode() converts bytes to lowercase hex stringencode_upper() converts bytes to uppercase hex stringdecode() converts hex string to bytes, handles both casesFromHex trait for decoding into specific typesToHex trait for encoding with more controlInvalidHexCharacter and OddLength0x prefix manually before decodingserde feature for automatic hex serialization