pub unsafe fn into_box(self, len: usize) -> Box<[MaybeUninit<T>], A> {
let me = ManuallyDrop::new(self);
unsafe {
let slice = slice::from_raw_parts_mut(me.ptr() as *mut MaybeUninit<T>, len);
Box::from_raw_in(slice, ptr::read(&me.alloc))
}
}
This code comes from Rust's standard lib which is a member function of RawVec. I have three questions:
Variable me is created via ManuallyDrop::new(self) and it has a type of ManuallyDrop<T>. But I cannot find any function that named ptr() in ManuallyDrop's source;
How does xxx as *mut MaybeUninit<T> works? The same one is as *const XXX, what is the difference?
slice::from_raw_parts_mut() has the prototype of :
It accepts a parameter of type *const T. So, for the example above, what T implies to here? MaybeUnint<T> or T? If it is T, how is it implicitly converted?
The ManuallyDrop type implements Deref so you can call methods on the underlying type directly even if wrapped in a ManuallyDrop. That's where the ptr method is defined.
The cast works like any other raw pointer cast. It converts the raw pointer to an *mut MaybeUninit<T>.
You are mixing up from_raw_parts and from_raw_parts_mut. The _mut variant accepts a *mut raw pointer. The T in the prototype corresponds to MaybeUninit<T> in the into_box code, so the Ts correspond to different things in the two contexts.
Yes, ManuallyDrop implements Deref trait. But what is the relationship between this trait and ptr() function? The only method defined by Deref trait is deref(). Any rule exist for this underlying?
Does that ptr conversion can happen between any two types? Such as:
Nothing by itself. But if a type T implements Deref, and you have a reference to that type, then it can be automatically coerced into a reference of type &<T as Deref>::Target, and so you can call the methods of that type.
You can cast between raw pointers to many different types, but it's usually not a good idea unless you really know what you are doing. For instance, in the example above, you just told the compiler that the pointer y points to an 8-byte-long and 8-byte-aligned storage, whereas it is only pointing to an object of 4 bytes and an alignment of 4 bytes. Dereferencing that incorrectly converted pointer would have disastrous consequences (it is Undefined Behavior in action).
The ptr method is defined here. You can call it because a ManuallyDrop<RawVec<...>> has access to all methods on RawVec due to the use of Deref.
Yes. However, the compiler will try to help you and requires a double cast when the types don't match.
let y = (&x) as *const u32 as *const f64;
Of course, actually using this raw pointer would be incorrect and can wreck all sorts of havoc because the memory it points at is not valid for the f64 type (it's too small, alignment is wrong). However, this would be ok since the u64 and f64 types are equally large and have the same alignment:
fn main() {
let x: u64 = 1024;
let y = (&x) as *const u64 as *const f64;
println!("{}", unsafe { *y });
}
This is essentailly a transmute. Of course, the bits that correspond to 1024 mean something completely different if you treat them as a floating point number, in this case meaning a ridiculously small number.