Why can't we have DST enums?


#1

I was trying to implement an unsized type and realized that Option<T> requires T: Sized. Thinking this was some arbitrary requirement, I tried implementing my own Option and realized that one simply can’t unsize enums. Specifically, I was trying to do the following:

use std::any::Any;

struct Struct<A: ?Sized + Any>(A);

enum Enum<A: ?Sized + Any> {
    Some(A),
    None,
}

fn main() {
    // Works.
    let s = Struct(0);
    let s = &s as &Struct<Any>;

    // Doesn't work (non-scalar cast).
    let e = Enum::Some(0);
    let e = &e as &Enum<Any>;
}

Can someone explain why I can’t do this?


#2

You can probably use a Box so that the enum owns the value but it is stored externally, as in:

enum Enum<A> {
    Some(Box<A>),
    None
}

#3

Basically, I’m trying to make something object safe and I don’t want to allocate:

use std::ops::{Deref, DerefMut};

trait Trait {}

impl Trait for i32 {}

struct View<'a>(&'a mut Wrapper<Trait + 'a>);

struct Wrapper<A: ?Sized + Trait>(A);

impl<'a> Wrapper<Trait + 'a> {
    fn view<'b>(&'b mut self) -> View<'b> where 'a: 'b {
        View(self as &'b mut Wrapper<Trait + 'b>)
    }
}

// I shouldn't have to play this deref dance but that's another issue...
impl<'a, T: Trait + 'a> Deref for Wrapper<T> {
    type Target = Wrapper<Trait + 'a>;
    fn deref(&self) -> &Self::Target {
        self as &Self::Target
    }
}

impl<'a, T: Trait + 'a> DerefMut for Wrapper<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self as &mut Self::Target
    }
}

fn dst_call(value: &mut Wrapper<Trait>) {
    let _ = value.view();
}

fn main() {
    let mut sized = Wrapper(0);
    let _ = sized.view();
    dst_call(&mut sized);
}

DST Receivers/Idempotent unsize
#4

If you want to store something that does not implement size, you have to store externally in the heap because we do not know its actual size. So you either know its size in which case you can store it in the collection, or you do not, in which case you need to store it in the heap.


#5

No. Everything I want to store on the stack is sized (&Struct<Any>, &Enum<Any>, Struct<i32>, Enum<i32>). I’m just trying to cast a &Enum<i32> to an &Enum<Any>.

The code I posted above does work. I’m just wondering why I can’t do this with enums.


#6

These two statements seem to contradict. You are saying all the things you want to store are sized? Well then they will implement Sized and therefore the Sized requirement for the enum is no problem?


#7

You can’t put DSTs on the stack but you can make references to sized values on the stack and cast these to references to DSTs.

use std::fmt::Debug;

fn debug(value: &Debug) {
    // `Debug` is a DST.
    // `&Debug` is Sized.
    println!("{:?}", value);
}

fn main() {
    // These are Sized.
    let a = 0i32;
    let b = 0i64;

    debug(&a as &Debug);
    debug(&b as &Debug);
}

That’s all I’m doing here.


#8

I don’t understand, a reference is not dynamically sized (only the data it points to is).

Ah, Debug is a trait object, which has no size. It looks like a reference, but it is an existential type.

The answer is probably that you need to add Sized to the bounds on the trait object.


#9

Actually I am not sure why the struct case works:

let s = &s as &Struct<Any>;

Any is a Trait, but you are not using it as a trait-object so how can it be the generic parameter to a struct? There is some magic going on here.


#10

related RFC-issue on unsized enums