Earlier quoted context omitted.
I don't see anything unsafe here. Merely storing a pointer, whatever it may be, doesn't violate memory-safety. Dereferencing a pointer in some cases does. The article contains one of those cases: use-after-free. If the pointer wasn't dereferenced, all code would be fine, including the line with ".as_ptr()".
What else do you do with a pointer, other than eventually dereference it? It points to invalid memory as soon as the underlying object is freed. It’s hard to think of a legitimate usage at that point. You’d hope that’s exactly the kind of thing the borrow checker would be able to warn you about. One situation where you might conceivably want to do something with that dangling pointer is if you were writing an allocat…
I could check whether two objects were stored at the same location (like comparing id-s in Python). Or I could write a crappy random number generator based on the pointers that I get from heap allocations. Or I could inspect the memory layout of a struct based on member offsets (pointer::offset_from method - perfectly safe).
Pointers whose pointees are tracked by the compiler are called "references". If you fetch a pointer instead, then you opt-out of this tracking.
> One situation where you might conceivably want to do something with that dangling pointer is if you were writing an allocator. But in this particular example, we’re not writing an allocator; and if we were, we wouldn’t call .as_ptr() on one of the allocated objects — that would be working at the wrong level of abstraction.
I can see someone writing an allocator based on std::vec::Vec, and then ".as_ptr()" is useful.
> If you really do just need to store the value and not dereference it, shouldn’t it be stored as some integer type? Then to dereference it, you’d first need to cast it to a pointer, and that operation could be marked unsafe.
You can't dereference a pointer in Rust per se. You need to convert it to a reference and then use the reference. This conversion is marked unsafe in Rust. There are also some "read" methods defined on pointers - also marked unsafe. But in the example in the article the pointer was dereferenced by C code, so Rust can't do anything about it.
Now imagine how an ".as_ptr()" which prevents taking pointers to temporary objects would need to be implemented. Well, you'd need to be able to write a method in Rust, which couldn't be called on temporaries. So the type-system would need to be able to express something like
fn as_ptr(&'non-temporary self) -> *Self
But then you'd be able to write this just fine: let ptr;
{
let path = CString::new(filename.as_str()).unwrap();
ptr = path.as_ptr();
}
Because path is not a temporary in the above code. The issue is ptr outlives the lifetime of path. So if the type-system had a special facility for avoiding pointers to temporaries, it would be trivially circumventable by creating a variable. Doesn't sound very human-friendly and doesn't sound like a big gain. This complexity doesn't pay for itself. We really need lifetimes (which we already have for references) for that.The solutions here are:
1. Borrow checker for objects with predictable lifetimes.
2. Smart pointers for objects with hard-to-predict lifetimes.
3. For calling C code, being careful, because it's C code and we can't fix C. Also, a linter with checks for common errors like those.