What do these lifetime requirements mean?


#1

rustc rejects the following code with an error I don’t understand:

pub struct Lamp<'a>(&'a i32);

pub trait Light {}
impl<'a> Light for Lamp<'a> {}

fn to_trait_ptr<'a>(p: *mut Lamp<'a>) -> *mut Light {
    p as *mut Light
}

fn main() {}

I was hoping to convert a pointer-to-concrete-type to a pointer-to-trait. I’d like a fat pointer, so I can call Light methods through the pointer, getting Lamp behavior.

The error message is:

lamp.rs:7:15: 7:20 error: cannot infer an appropriate lifetime due to conflicting requirements [E0495]
lamp.rs:7     p as *mut Light
                        ^~~~~
lamp.rs:6:53: 8:2 note: first, the lifetime cannot outlive the lifetime 'a as defined on the block at 6:52...
lamp.rs:6 fn to_trait_ptr<'a>(p: *mut Lamp<'a>) -> *mut Light {
lamp.rs:7     p as *mut Light
lamp.rs:8 }
lamp.rs:7:5: 7:6 note: ...so that the type `Lamp<'a>` will meet its required lifetime bounds
lamp.rs:7     p as *mut Light
              ^
note: but, the lifetime must be valid for the static lifetime...
lamp.rs:7:5: 7:20 note: ...so that the declared lifetime parameter bounds are satisfied
lamp.rs:7     p as *mut Light
              ^~~~~~~~~~~~~~~
error: aborting due to previous error

I don’t understand either of the two conflicting requirements. What’s going on here?


#2

A trait object implicitly (or explicitly) has a lifetime bound on the data it contains, so that the compiler knows how long the type-erased data is valid. The lifetime can be explicitly specified with +'foo, e.g. *mut (Trait + 'foo), but there’s sugar that allows omitting it, with a default. For trait objects like *mut Trait or Box<Trait> that default is 'static, i.e. it’s as if *mut (Trait + 'static) was written, and means that one cannot erase data containing any lifetimes shorter than 'static. However, one can override the default:

fn to_trait_ptr<'a>(p: *mut Lamp<'a>) -> *mut (Light+'a) {
    p
}

The *mut (Light + 'a) trait object says "I may contain data that lives as short as 'a" and hence the compiler will stop it escaping outside the period of validity (i.e. more restrictive in how you use it), but allows you to put the short lived data into it (i.e. less restrictive in how you create it).