You should stop telling people that safe rust is always safe


#1

Was reading this in The Rustonomicon and couldn’t help laughing:

Safe Rust is the true Rust programming language. If all you do is write Safe Rust, you will never have to worry about type-safety or memory-safety. You will never endure a null or dangling pointer, or any of that Undefined Behavior nonsense.
That’s totally awesome.

And yet this is “safe” Rust that is totally wrong and will produce a memory corrupted string:

let a:LPCSTR = CString::new("Some string for Windows API").unwrap().as_ptr();

This took me two days to debug, no warning, no error.


#2

That raw pointer won’t do anything bad until you dereference it, which can only be done with an unsafe block.

It’s better to pass things around as references as long as possible if you want lifetime checks, and only devolve to a pointer when you must for FFI.


#3

The problem here is (also stated in the docs)

https://doc.rust-lang.org/std/ffi/struct.CString.html#method.as_ptr

The returned pointer will be valid for as long as self is and points
to a contiguous region of memory terminated with a 0 byte to represent
the end of the string.

If you do

let t = CString::new("Some string for Windows API").unwrap();
let a  = t.as_ptr();

This will work as self is now alive, while otherwise it won’t be.


#4

Yes, I read the docs later and realized what the problem was, I guess I was assuming that temporary object would be kept alive for as long as I needed it. And it’s true that actual corruption happened in unsafe block…So safe as long as I don’t dereference it, got it.


#5

Yeah. I agree that this one is a annoying as I have ran into the same issue. While I understand why it works the way it does it’s still annoying.


#6

There, fixed it for you.


#7

FWIW, it looks like clippy has a lint for this.


#8

Judging by the results of the search “unwrap().as_ptr()” on github, not all use clippy.

Is it safe?

open(CString::new("tempfile").unwrap().as_ptr(), O_RDWR, 0)

#9

The CString lifetime is the length of the entire open() function call, correct? This looks safe to me.

But it requires that open() does not maintain a reference to that pointer that would outlive the function call.


#10

That’s horrible… let’s do something about it: https://github.com/rust-lang/rust/issues/34111