Decomposing references usized types


#1

Is the layout of trait objects defined, for example say I have a trait object &dyn std::fmt::Any, is there any way for me to extract out the v-table? How about slices, can I decompose a &[T] into a thin pointer *const T and a usize representing the size? Importantly, is it UB to even try to do so?


#2
let ptr = slice.as_ptr();
let len = slice.len();

#3

Ok, how about trait objects? Is there a way to decompose those?


#4

std::mem::transmute and unions can do this.
Playground


#5

Oh, I think I did something wrong when I did that, because I got MIRI to complain. Is the representation of trait objects stable?


When I ran this code through MIRI it complained, this is a slight modification of yours where I reconstructed the trait object and tried to use it.


#6

For traits you have https://doc.rust-lang.org/std/raw/struct.TraitObject.html but it’s not stable yet or unsafe but the layout is not officially defined


#7

To be clear, I am trying to create a smart pointer, and I want to be able to handle ?Sized types. So is there a general way to get a thin pointer and the rest of the fat pointer’s metadata? From the looks of what @leudz says, I don’t think so.


#8

That looks like an acceptable solution for me for the time being.


#9

What are you trying to achieve with this smart pointer? Just storing a pointer/reference to something and deleting it when there aren’t any references to it (Like Rc)? Then just use the *const T approach. Playground


#10

Relative pointers, I can’t use normal pointers for it as that defeats the purpose.


If you want to take a look, I published it to crates.io here https://crates.io/crates/rel-ptr


#11

Hmm, so looking at your example you want to be able to pass things like &dyn Trait and &[T] to the relative pointer so as to store them relative to the pointer object?


To be completely honest I don’t really understand this; where is the data stored and I have a bugging feeling that this can have quite abit of UB associated with it.


#12

Exactly


It might be UB, I have to run it through MIRI and get it audited by people who know more about UB than I do. But this is an interesting idea so I thought I would try it out in Rust.


#13

Okay, but when I pass, say, a &dyn std::fmt::Debug, what is it expected to do? Would it copy the raw dyn std:::fmt::Debug? What about memory safety because then that’d be cloning a potentially non-Clone object which is under a reference. In the case of &[T] it just doesn’t make sense. Unless you wanted the type that the pointer stores be T and it be able to allocate a &[T]


#14

No, I would like to decompose the &dyn TraitObject into two parts, the thin pointer and its v-table. I will then store the v-table and reconstruct the pointer on demand. Similarly for slices, but as I didn’t notice earlier, slices have an api for doing this via as_ptr, len, and from_raw_parts.


#15

Unfortunately, vtables are mostly compiler magic past the fourth element. Take a look at this blog post from 2015, and this more recent blog post from 2017. These both have more information on vtables. It seems that there are three concrete parts to the vtable: the drop function, the size, and the align. The rest of the vtable is up to the specific trait’s function signatures.


#16

Be careful here. Just because you can code such a fat-pointer creation doesn’t mean that the result won’t be UB. This post in IRLO earlier today by @RalfJung explains why being able to do something at the machine level is insufficient to avoid UB.

In summary, Rust compiles to a virtual machine, which in turn is translated to code for the target real machine (and OS, if any). If you break a virtual machine primitive by decomposing it incompatibly, a heavily-optimizing compiler may munge your code without notice.


#17

Yes, I am aware of that, I followed that thread as well. I was just curious if that would work at all. It seems like it won’t so I likely won’t be adding trait object support to relative pointers. That said there are future proposals that could standardize this and make it possible to do this, for example Custom DSTs could formalize the layout of trait objects or lead to the formalization of trait objects. This would could allow me to allow trait objects without incurring UB.


#18

Looking at what @leudz posted earlier, std::raw::TraitObject can be used to decompose and recompose a trait object, but it will require nightly (which is fine). But I don’t think I will add it just yet, I still need to make sure the rest of my lib is free of UB.


#19

I believe https://github.com/rust-lang/rfcs/pull/2580 is relevant to this thread too.


#20

Landing that RFC would make such constructions definitely not UB.