How does the internal representation of `str` work?
โ Rust ๐ 2026-07-14 ๐ค surdeus ๐๏ธ 2I'm currently working on a lint for clippy that discourages you from giving pointers to Rust strs to extern "C" functions, since that generally leads to UB (Lint str-ptr-in-c-abi discourage str pointers in C ABI fns by fpdotmonkey ยท Pull Request #17401 ยท rust-lang/rust-clippy ยท GitHub). However, I was testing out what actually happens when you do this, and I got a curious result.
let fmt = std::ffi::CString::new("hello.as_ptr() as *const _ == %s\n".as_bytes()).unwrap();
let hello = "Hello!";
/// SAFETY: fmt is a null-terminated string and requires 1 stringy variadic arg
/// that arg is hello, which is a pointer to a Rust string, which WILL CAUSE UB
unsafe { libc::printf(fmt.as_ptr() as *const _, hello.as_ptr() as *const _) };
Now, my mental model of str is that it looks something like this,
struct str {
len: usize,
start: *mut char,
}
which would suggest that the above would print this,
hello.as_ptr() as *const _ == \x{6}Hello!{overread nonsense}
but what it actually shows is this,
"Hello!".as_ptr() as *const _ == Hello!{overread nonsense}
Even for longer strings where '\x{N}' would be a printing character, it doesn't do anything. So what's happening under the hood? I looked through the source code and I didn't find any definition for str, at least in core/str/mod.rs
16 posts - 6 participants
๐ท๏ธ Rust_feed