Imagine we have a private library to which we can make no changes, which contains some type that is not exposed in the library module.
// lib.rs
mod inner {
pub struct SecretData {
value: u32,
}
impl SecretData {
fn new(value: u32) -> Self {
SecretData { value }
}
}
pub fn create_secret() -> SecretData {
SecretData::new(42)
}
}
pub use inner::create_secret;
In our application, we want to create a secret using the function provided by the library and then print out the secret.
// main.rs
use my_crate::create_secret;
fn main() {
let secret = create_secret();
println!("The secret is: {:?}", secret);
}
The above code in the main function will lead to an error indicating the Debug trait has not been implemented for the struct SecretData.
Is there any way in which we can print out the value of secret
?
It's not possible in general.
To be able to print meaningful data from a struct, you need to:
std::fmt
, or a custom method like Path::display
SecretData
so you can safely read & print it yourself. That excludes all repr(Rust)
structs.For a struct that you do know the memory layout of, you can transmute to a struct that does implement a displaying trait:
mod inner {
#[repr(C)]
pub struct SecretData {
value: u32,
}
impl SecretData {
fn new(value: u32) -> Self {
SecretData { value }
}
}
pub fn create_secret() -> SecretData {
SecretData::new(42)
}
}
use inner::create_secret;
#[repr(C)]
#[derive(Debug)]
struct NotSoSecret {
v: u32,
}
fn main() {
let secret = create_secret();
// # Safety:
// Both `SecretData` and `NotSoSecret` are `#[repr(C)]` structs with a single `u32` field so they both have the same memory layout.
println!("The secret is: {:?}", unsafe {
std::mem::transmute::<_, NotSoSecret>(secret)
});
}
Note: Both #[repr(C)]
(or another way to ensure the same memory layout) are required because otherwise the memory layout is unspecified which makes transmute
UB.