[SOLVED] Initialize an unsized struct

I've just learnt that structs can have their last field as unsized, I've made one as following just for playing around with the concept.

use std::fmt::Display;
struct Show {
    a: usize,
    b: dyn Display,
}

But no matter how I go about it, I just cannot initialize it. E.g.:

    let x = Box::new(Show { a: 1, b: String::new() as dyn Display });

It throws error, the size for values of type (dyn std::fmt::Display + 'static) cannot be known at compilation time.
The only implementation that I've seen for unsized struct is of RcBox which is a private struct used by std::rc::Rc . And it doesn't exactly make it's value field unsized (by using something like dyn Trait, like I have), but rather allows it to be questionably sized by being generic. Another point worth noting is this impl block's generic T input, as far as I understand, is bound to be Sized, so at the end of the day, Rc::new takes a sized input which defies the purpose of allowing it's generic parameter to be ?Sized.
What do I want?
Some way to initialize my Show struct.

From the nomicon

Currently the only properly supported way to create a custom DST is by making your type generic and performing an unsizing coercion:

This is the code the nomicon has as an example of that

struct MySuperSliceable<T: ?Sized> {
    info: u32,
    data: T,
}

fn main() {
    let sized: MySuperSliceable<[u8; 8]> = MySuperSliceable {
        info: 17,
        data: [0; 8],
    };

    let dynamic: &MySuperSliceable<[u8]> = &sized;

    // prints: "17 [0, 0, 0, 0, 0, 0, 0, 0]"
    println!("{} {:?}", dynamic.info, &dynamic.data);
}
1 Like

Beware that actually using it is difficult for now.

The vast majority of the time you'll want either

struct Show {
    a: usize,
    b: Box<dyn Display>,
}

or

struct Show<'a> {
    a: usize,
    b: &'a dyn Display + 'a,
}

instead.

After all, if you make Show a DST you'll need to use it behind a reference or inside a Box anyway.

2 Likes

@semicoleon I guess there isn't much space to argue if Nomicon itself accepts that "Although such a type is largely useless without a way to construct it. Currently the only properly supported way to create a custom DST is by making your type generic and performing an unsizing coercion".

Thanks for the prompt answer.

I publish the slice-dst crate, which enables constructing unsized structs with slice tails ([T]) directly. I also have the indyn crate which is an old nightly-only experiment also in the same space of custom DSTs.

You can directly create non-generic structs with slice tails, but it requires doing allocation and piecewise initialization manually. There is currently no stable way to construct structs with dyn Trait tails except via unsizing.

I've done a significant chunk of work showing what's currently possible and what could be possible with a few more stabilizations. And I'll still say: just use the unsizing.

In your case it could be something like

struct Show<T: ?Sized = dyn Display> {
    a: usize,
    b: T,
}

impl<T: Display> Show<T> {
    pub fn new_box(a: usize, b: T) -> Box<Show> {
        Box::new(Show { a, b })
    }
}

impl Show {
    // the other stuff
}
2 Likes

So, the conclusion still sticks to taking a Sized input for constructing such structs, as T does have a Sized bound in quoted impl block. Hence, to some extent defying purpose of keeping the struct as a DST in the first place.