I'm writing very basic AI system in Rust. It's main components are:
Action
s, 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 commonActionsContainer
.
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)
}
}
}
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.
I've found the solution:
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.