3
votes

I'm trying to use a struct (Outer), which is statically defined. Outer has a method that takes a reference to a struct (Context), where the lifetime of Context is shorter than Outer.

The following code can also be found at this rust playground:

use std::marker::PhantomData;

trait Doable {
    fn do_thing(&self) -> u8;
}

struct Stuff(u8);

struct Context<'a> {
    stuff: &'a mut Stuff,
}

impl<'a> Doable for Context<'a> {
    fn do_thing(&self) -> u8 {
        self.stuff.0 + 1
    }
}

struct Outer<'a, T> {
    buffer: &'a mut [u8],
    used: u8,
    phantom_context: PhantomData<T>,
}

impl<'a, T> Outer<'a, T>
where T: Doable
{
    fn use_context(&'a mut self, context: &mut T) {
        self.used = context.do_thing();
    }
}

fn use_outer_with_context(context: &mut Context) {
    static mut buffer: [u8; 64] = [0u8; 64];
    static mut outer: Outer<'static, Context> = unsafe {
        Outer {
            buffer: &mut buffer,
            used: 0,
            phantom_context: PhantomData,
        }
    };

    unsafe {outer.use_context(context)};
}

fn main() {

    let mut s = Stuff(4);

    let context = Context {stuff: &mut s};

    use_outer_with_context(&mut context);

}

When I try to compile this code, I get the following error:

error[E0621]: explicit lifetime required in the type of `context`
  --> src/lib.rs:45:31
   |
35 | fn use_outer_with_context(context: &mut Context) {
   |                                    ------------ help: add explicit lifetime `'static` to the type of `context`: `&mut Context<'static>`
...
45 |     unsafe {outer.use_context(context)};
   |                               ^^^^^^^ lifetime `'static` required

I'm having difficulty understanding why the rust compiler expects/requires the lifetime of context to be the same lifetime ('static) as outer. Shouldn't it be ok for outer to outlive context, as it's only using context for the lifetime of the use_context() method?

1

1 Answers

3
votes

There is an elided lifetime here:

static mut outer: Outer<'static, Context> = unsafe {
                                 ^^^^^^^

Since this is a static variable, the compiler will infer the 'static lifetime, hence the full type of outer is Outer<'static, Context<'static>>.

Consequently, in the call to use_context, the type of the context parameter is &mut Context<'static>. However, use_outer_with_context is passing in a &mut Context<'a> (the 'a is elided in the source).

The issue here is that T needs to be specified in advance, whereas in fact, T might change from call to call, due to the lifetime in Context<'a>.

Your sample can be made to work by moving T from the struct to the use_context method.

struct Outer<'a> {
    buffer: &'a mut [u8],
    used: u8,
}

impl<'a> Outer<'a>
{
    fn use_context<T>(&mut self, context: &mut T)
    where
        T: Doable,
    {
        self.used = context.do_thing();
    }
}

fn use_outer_with_context(context: &mut Context) {
    static mut buffer: [u8; 64] = [0u8; 64];
    static mut outer: Outer<'static> = unsafe {
        Outer {
            buffer: &mut buffer,
            used: 0,
        }
    };

    unsafe {
        outer.use_context(context);
    }
}

Note: I changed &'a mut self to &mut self on Outer::use_context; the 'a is unnecessary and can lead to compiler errors.

There might be a reason why you wouldn't want to move T to the individual method. In that case, then I don't know of a viable solution. If Rust had generic associated types, then it would be possible to represent Context with the lifetime parameter pending application. Unfortunately, generic associated types are not implemented as of Rust 1.41.0.