Rust 1.26.0 and up
The :x? "debug with hexadecimal integers" formatter can be used:
let data = b"hello";
// lower case
println!("{:x?}", data);
// upper case
println!("{:X?}", data);
let data = [0x0, 0x1, 0xe, 0xf, 0xff];
// print the leading zero
println!("{:02X?}", data);
// It can be combined with the pretty modifier as well
println!("{:#04X?}", data);
Output:
[68, 65, 6c, 6c, 6f]
[68, 65, 6C, 6C, 6F]
[00, 01, 0E, 0F, FF]
[
0x00,
0x01,
0x0E,
0x0F,
0xFF,
]
If you need more control or need to support older versions of Rust, keep reading.
Rust 1.0 and up
use std::fmt::Write;
fn main() {
let mut s = String::new();
for &byte in "Hello".as_bytes() {
write!(&mut s, "{:X} ", byte).expect("Unable to write");
}
println!("{}", s);
}
This can be fancied up by implementing one of the formatting traits (fmt::Debug, fmt::Display, fmt::LowerHex, fmt::UpperHex, etc.) on a wrapper struct and having a little constructor:
use std::fmt;
struct HexSlice<'a>(&'a [u8]);
impl<'a> HexSlice<'a> {
fn new<T>(data: &'a T) -> HexSlice<'a>
where
T: ?Sized + AsRef<[u8]> + 'a,
{
HexSlice(data.as_ref())
}
}
// You can choose to implement multiple traits, like Lower and UpperHex
impl fmt::Display for HexSlice<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for byte in self.0 {
// Decide if you want to pad the value or have spaces inbetween, etc.
write!(f, "{:X} ", byte)?;
}
Ok(())
}
}
fn main() {
// To get a `String`
let s = format!("{}", HexSlice::new("Hello"));
// Or print it directly
println!("{}", HexSlice::new("world"));
// Works with
HexSlice::new("Hello"); // string slices (&str)
HexSlice::new(b"Hello"); // byte slices (&[u8])
HexSlice::new(&"World".to_string()); // References to String
HexSlice::new(&vec![0x00, 0x01]); // References to Vec<u8>
}
You can be even fancier and create an extension trait:
trait HexDisplayExt {
fn hex_display(&self) -> HexSlice<'_>;
}
impl<T> HexDisplayExt for T
where
T: ?Sized + AsRef<[u8]>,
{
fn hex_display(&self) -> HexSlice<'_> {
HexSlice::new(self)
}
}
fn main() {
println!("{}", "world".hex_display());
}
Answer from Shepmaster on Stack OverflowRust 1.26.0 and up
The :x? "debug with hexadecimal integers" formatter can be used:
let data = b"hello";
// lower case
println!("{:x?}", data);
// upper case
println!("{:X?}", data);
let data = [0x0, 0x1, 0xe, 0xf, 0xff];
// print the leading zero
println!("{:02X?}", data);
// It can be combined with the pretty modifier as well
println!("{:#04X?}", data);
Output:
[68, 65, 6c, 6c, 6f]
[68, 65, 6C, 6C, 6F]
[00, 01, 0E, 0F, FF]
[
0x00,
0x01,
0x0E,
0x0F,
0xFF,
]
If you need more control or need to support older versions of Rust, keep reading.
Rust 1.0 and up
use std::fmt::Write;
fn main() {
let mut s = String::new();
for &byte in "Hello".as_bytes() {
write!(&mut s, "{:X} ", byte).expect("Unable to write");
}
println!("{}", s);
}
This can be fancied up by implementing one of the formatting traits (fmt::Debug, fmt::Display, fmt::LowerHex, fmt::UpperHex, etc.) on a wrapper struct and having a little constructor:
use std::fmt;
struct HexSlice<'a>(&'a [u8]);
impl<'a> HexSlice<'a> {
fn new<T>(data: &'a T) -> HexSlice<'a>
where
T: ?Sized + AsRef<[u8]> + 'a,
{
HexSlice(data.as_ref())
}
}
// You can choose to implement multiple traits, like Lower and UpperHex
impl fmt::Display for HexSlice<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for byte in self.0 {
// Decide if you want to pad the value or have spaces inbetween, etc.
write!(f, "{:X} ", byte)?;
}
Ok(())
}
}
fn main() {
// To get a `String`
let s = format!("{}", HexSlice::new("Hello"));
// Or print it directly
println!("{}", HexSlice::new("world"));
// Works with
HexSlice::new("Hello"); // string slices (&str)
HexSlice::new(b"Hello"); // byte slices (&[u8])
HexSlice::new(&"World".to_string()); // References to String
HexSlice::new(&vec![0x00, 0x01]); // References to Vec<u8>
}
You can be even fancier and create an extension trait:
trait HexDisplayExt {
fn hex_display(&self) -> HexSlice<'_>;
}
impl<T> HexDisplayExt for T
where
T: ?Sized + AsRef<[u8]>,
{
fn hex_display(&self) -> HexSlice<'_> {
HexSlice::new(self)
}
}
fn main() {
println!("{}", "world".hex_display());
}
Use hex::encode from the hex crate.
let a: [u8;4] = [1, 3, 3, 7];
assert_eq!(hex::encode(&a), "01030307");
[dependencies]
hex = "0.4"
You can also implement hex encoding and decoding yourself, in case you want to avoid the dependency on the hex crate:
use std::{fmt::Write, num::ParseIntError};
pub fn decode_hex(s: &str) -> Result<Vec<u8>, ParseIntError> {
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16))
.collect()
}
pub fn encode_hex(bytes: &[u8]) -> String {
let mut s = String::with_capacity(bytes.len() * 2);
for &b in bytes {
write!(&mut s, "{:02x}", b).unwrap();
}
s
}
Note that the decode_hex() function panics if the string length is odd. I've made a version with better error handling and an optimised encoder available on the playground.
You could use the hex crate for that. The decode function looks like it does what you want:
fn main() {
let input = "090A0B0C";
let decoded = hex::decode(input).expect("Decoding failed");
println!("{:?}", decoded);
}
The above will print [9, 10, 11, 12]. Note that decode returns a heap allocated Vec<u8>, if you want to decode into an array you'd want to use the decode_to_slice function
fn main() {
let input = "090A0B0C";
let mut decoded = [0; 4];
hex::decode_to_slice(input, &mut decoded).expect("Decoding failed");
println!("{:?}", decoded);
}
or the FromHex trait:
use hex::FromHex;
fn main() {
let input = "090A0B0C";
let decoded = <[u8; 4]>::from_hex(input).expect("Decoding failed");
println!("{:?}", decoded);
}