Hello. I've come across two examples that are confusing me. First is used by the anymap crate, another is in the std lib itself.
Consider the following from the popular anymap crate (Self
is dyn Any
):
unsafe fn downcast_unchecked<T: 'static>(self: Box<Self>) -> Box<T> {
Box::from_raw(Box::into_raw(self) as *mut T)
}
unsafe fn downcast_ref_unchecked<T: 'static>(&self) -> &T {
&*(self as *const Self as *const T)
}
This is used to downcast types from dyn Any
to some T
.
The similar example using std, copied from the docs:
use std::any::Any;
fn print_if_string(value: Box<dyn Any>) {
if let Ok(string) = value.downcast::<String>() {
println!("String ({}): {}", string.len(), string);
}
}
let my_string = "Hello World".to_string();
print_if_string(Box::new(my_string));
print_if_string(Box::new(0i8));
Both use Box::into_raw
on a Box<dyn Any>
and then converts the raw ptr to a Box<T>
using Box::from_raw
(and the 2nd example from anymap does something similar through unsafe casting).
Now, the reason I'm confused about this is because I know that a ptr to a dyn Trait
is a fat ptr, of completely different size to a ptr to a concrete T
(double the size). And I also know for a fact that the fat ptr layout is unspecified. Who's to say the fat ptr won't change so that the first aligned ptr will actually point to the vtable? Isn't this also leaking memory because we're completely forgetting the vtable ptr in the fat ptr?
For me, this sounds the exact same as the following:
std::mem::transmute::<Box<dyn Any>, Box<T>>(b)
which actually doesn't compile because of different sizes (16 vs 8). But also similar to this which compiles:
std::mem::transmute::<&Box<dyn Any>, &Box<T>>(b)
^ But this to me looks like UB, depending on the fact that the first aligned ptr of a fat ptr is the one that pts to T (but unlike the consuming downcast, doesn't suffer from a mem leak).
So my question is, how is this not depending on UB, especialy for anymap. Because for the std lib case one can claim that it's exactly because it's the std lib it has the right to depend on unspecified spec (fat ptr layout). But even then, it doesn't explain the mem leak I think I'm seeing (from just reasoning about the code).
Am I missing something?