Why the code is forbidden

The code is taken from learing rust by @quinedot

As it says,

fn foo<'s, 'l: 's>(v: std::cell::Cell<Box<dyn Display + 'l>>) -> std::cell::Cell<Box<dyn Display + 's>> {
    v
}

can be compiled, but

fn foo<'s, 'l: 's>(v: std::cell::Cell<Box<Box<dyn Display + 'l>>>) -> std::cell::Cell<Box<Box<dyn Display + 's>>> {
    v
}

can't.

It does, but I am thinking is the 2nd one really dangerous?

The Variance is so called unsized coercions in the book, though is really nothing to do with the sizeness.

I think the reason underhook it because dyn Display + 's is just an unknown type, you can't assign some reference to the type under dyn Display, you can only annotate the type's lifetime as whole.

But when it's a member of struct, it is different; because T<dyn Trait + 'long> means T maybe as long as 'long; Cell<Box<dyn Display + 's>>, for example, dyn Display has a life 's, then Box<<dyn Display + 's>> can't exceed the 's too, then Cell<Box<dyn Display + 's>> is same.

Why

fn foo<'s, 'l: 's>(v: std::cell::Cell<Box<dyn Display + 'l>>) -> std::cell::Cell<Box<dyn Display + 's>> {
    v
}

is accepted because the cell is passed by value, after Cell<'l> was considered as Cell<'s>, it can't be move out to outer scope of 's.

But I think the same applies to Cell<Box<Box<dyn Display + 's>>>, there is no chance for it to be moved out of 's, so seems there is no chance for it to result in dangerous things.

If we accept the existing "ultra-covariance" coercions (that aren't really variance) are sound, I can't think of why the nested one involving only lifetimes would not be sound.

But allowing such a coercion would have to be a special case instead of utilizing the same trait-based unsizing coercions as, say, supertrait upcasts (as those can change the vtable pointer). Which is how things work today.

2 Likes