“The parameter type `C` may not live long enough”,

2019-07-16 14:21发布

I'm writing very basic AI system in Rust. It's main components are:

  • Actions, which can be implemented by library user, for specific use,
  • Generic Context, which is passed to all actions, and only needs to live during the action execution,
  • ActionsContainer, which "globally" stores all possible actions,
  • System, which chooses the correct action and runs it. There are many systems, one for each agent. However, they share the same set of behaviours, so they all reference a common ActionsContainer.

Here is a minimum example which illustrates my problem.

// Generic system

trait Context {}

trait Action<C: Context> {
    fn run(&self, context: &mut C);
}

struct ActionsContainer<C: Context> {
    actions: Vec<Box<Action<C>>>,
}

struct System<'a, C: Context> {
    actions: &'a ActionsContainer<C>,
}

impl<'a, C: Context> System<'a, C> {
    fn run(&self, c: &mut C) {
        self.actions.actions[0].run(c);
    }
}

// Implementation

struct ContextImpl<'a> {
    x: &'a i32,
    y: i32,
}

impl<'a> Context for ContextImpl<'a> {}

struct ActionImpl {}

impl<'a> Action<ContextImpl<'a>> for ActionImpl {
    fn run(&self, c: &mut ContextImpl) {
        println!("Action!");
        c.y = c.x;
    }
}

// usage
fn main() {
    let container = ActionsContainer {
        actions: vec![Box::new(ActionImpl {})],
    };

    {
        let system = System {
            actions: &container,
        };

        {
            let x = 8;
            let mut context = ContextImpl { x: &x, y: 0 };

            system.run(&context);

            assert_eq!(context.y, context.x)
        }
    }
}

playground

The compiler complains:

error[E0309]: the parameter type `C` may not live long enough
  --> src/main.rs:14:5
   |
13 | struct System<'a, C: Context> {
   |                   -- help: consider adding an explicit lifetime bound `C: 'a`...
14 |     actions: &'a ActionsContainer<C>,
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
note: ...so that the reference type `&'a ActionsContainer<C>` does not outlive the data it points at
  --> src/main.rs:14:5
   |
14 |     actions: &'a ActionsContainer<C>,
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

However, C is not stored in Action. It only needs to live while run is executing. On the other hand, the Action does need to live as long as whole System. Is there any way to annotate this?

I suspect, it has something to do with Higher-Rank Trait Bounds, but I don't see how to use them here.

I've also tried to get rid of Action as a trait object and just use plain function references:

type Action<C> = fn(&mut C);

struct ActionsContainer<C: Context> {
    actions: Vec<&'static Action<C>>,
}

But the compiler error was pretty much the same.

1条回答
爷、活的狠高调
2楼-- · 2019-07-16 15:09

I've found the solution:

// Generic system

trait Context {}

trait Action<C: Context> {
    fn run(&self, context: &mut C);
}

struct ActionsContainer<A> {
    actions: Vec<Box<A>>,
}

struct System<'a, A: 'a> {
    actions: &'a ActionsContainer<A>,
}

impl<'a, A> System<'a, A> {
    fn run<C>(&self, c: &mut C)
    where
        C: Context,
        A: Action<C>,
    {
        self.actions.actions[0].run(c);
    }
}

// Implementation

struct ContextImpl<'a> {
    x: &'a i32,
    y: i32,
}

impl<'a> Context for ContextImpl<'a> {}

struct ActionImpl {}

impl<'a> Action<ContextImpl<'a>> for ActionImpl {
    fn run(&self, c: &mut ContextImpl) {
        println!("Action!");
        c.y = *c.x;
    }
}

// usage
fn main() {
    let container = ActionsContainer {
        actions: vec![Box::new(ActionImpl {})],
    };

    {
        let system = System {
            actions: &container,
        };

        {
            let x = 8;
            let mut context = ContextImpl { x: &x, y: 0 };

            system.run(&mut context);

            assert_eq!(context.y, *context.x)
        }
    }
}

Playground

Rust always assumes that traits mentioned in generic struct will be stored in that struct (hence my lifetime problems). If you are not intending to store the trait, do not mention it in struct definition. Instead, use more general bounds, and clarify them on the method, which defines appropriate lifetime.

查看更多
登录 后发表回答