Can someone explain this miri error: encountered a pointer but expected plain (non-pointer) bytes

Transmuting pointers is a red flag. It's almost never sound. Transmutation is not transitive: if transmute between T and U is valid, it doesn't imply that it's valid between &T and &U or Box<T> and Box<U> or anything like that. This is because transmutation deals with values, so when you transmute pointers, you are converting between the values of those pointers themselves, without regard to what they point to.

I'm curious as to what purpose this serves. I suspect what you are actually trying to do here is transmute the pointed value (i32) to another type ([u8; 4]), but do it indirectly instead of by-value. For that, you can just cast the pointers instead of transmuting, which will preserve provenance.

"enforcing" constraints doesn't mean that you get a runtime error. Miri is merely a tool that tries to make detection of UB more user-friendly. If you violate provenance rules, it's still UB even outside of Miri, it's just perhaps less apparent.

No. Drop doesn't run when you "pass ownership", it runs when the lifetime of a value ends. Transmute gives you back the same value that you passed, so it gives up ownership immediately after acquiring it.

It also pretty quickly gets to the several "don't do this" parts, with an illustration of the various creative ways of getting UB via pointer transmutation.

In particular, for Vec, it specifically says:

// Using transmute: this relies on the unspecified data layout of `Vec`, which is a
// bad idea and could cause Undefined Behavior.
// However, it is no-copy.
let v_transmuted = unsafe {
    std::mem::transmute::<Vec<&i32>, Vec<Option<&i32>>>(v_clone)
};

So don't just skim through the "interesting" parts of the code if you are trying to scrutinize the memory model. Read the prose too, because interpreting a "don't do this" example as a "you can do this" example is counter-productive.

5 Likes