8
votes

DST (Dynamically Sized Types) are a thing in Rust now. I have used them successfully, with a flexible last member which is known to the compiler (such as [u8]).

What I am looking to do, however, is to create a custom DST. Say, for example:

struct Known<S> {
    dropit: fn (&mut S) -> (),
    data: S,
}

struct Unknown {
    dropit: fn (&mut ()) -> (),
    data: (),
}

With an expected usage being Box<Known<S>> => Box<Unknown> => Box<Known<S>>, where the middleware need not know about concrete types.

Note: yes, I know about Any, and no I am not interested in using it.

I am open to suggestions in the layout of both Known and Unknown, however:

  1. size_of::<Box<Known>>() = size_of::<Box<Unknown>>() = size_of::<Box<u32>>(); that is it should be a thin pointer.
  2. dropping Box<Unknown> drops its content
  3. cloning Box<Unknown> (assuming a clonable S), clones its content
  4. ideally, fn dup(u: &Unknown) -> Box<Unknown> { box u.clone() } works

I have particular difficulties with (3) and (4), I could solve (3) with manually allocating memory (not using box, but directly calling malloc) but I would prefer providing an idiomatic experience to the user.

I could not find any documentation on how to inform box of the right size to allocate.

3
This thing is different enough from everything in the standard library (that I know of) that implementing it manually, with unsafe code, is probably the easiest, most reliable, and all around best option. It most likely won't fit into the existing DST scheme or into Box.user395760
There are exactly two types of unsized objects: slices ([T]), where it adds a length member; and trait objects (Trait, Trait + Send, &c.), where it adds a vtable including a destructor which knows how large an object to free. There is no mechanism for declaring your own variety of unsized objects.Chris Morgan
@ChrisMorgan: Humpf... I was kind of afraid of that... May I suppose thus that box allocates based on std::mem::size_of?Matthieu M.
At the moment, the answer is sadly “magic”.Chris Morgan
@ChrisMorgan: Would you formalize all this in an answer? After all, "Not possible" is a perfectly valid answer.Matthieu M.

3 Answers

4
votes

There are exactly two types of unsized objects at present: slices ([T]), where it adds a length member; and trait objects (Trait, Trait + Send, &c.), where it adds a vtable including a destructor which knows how large an object to free.

There is not currently any mechanism for declaring your own variety of unsized objects.

1
votes

At this point, you should seek inspiration from Arc::new_uinint_slice and Arc::from_ptr.

We've no nice mechanism to make custom DSTs play nicely together though, making Arc<Known<T>> nasty.

We still always create Arc<dyn Trait> with CoerceUnsized because you cannot make trait objects form DSTs currently.

1
votes

You could try using the vptr crate, which stores the vtable pointer with the data instead of with the pointer.