Reputation: 148
TL;DR Having problems with lifetime with nested objects. Code is below.
Long Version:
I'm writing a multiplayer game using ggez
, and I'm trying to create an abstraction layer for the input (to allow local and remote players play together).
In order to so, I've created an Input
trait, and implemented KeyboardInput
for local input, which uses ggez
keyboard state querying methods.
Now the tricky part: ggez
creates Context
object at startup, and expects a reference to it in most of the functions that is exposes.
because my KeyboardInput
implementation is using ggez
input method (specifically, is_key_pressed
), it has to pass &Context
to this method. However, since the trait itself should be generic, it won't need a Context
reference for any other implementation (for example, NetworkInput
).
my solution was to add a reference to Context
as field in the KeyboardInput
struct. However, that caused a lifetime error that I'm still unable to resolve.
I also tried to make the lifetime 'static
, but that did not work either.
here is the relevant code:
pub trait Input {
fn get_direction(&self) -> Direction;
}
pub struct KeyboardInput<'a> {
left_key: KeyCode,
right_key: KeyCode,
_ctx: &'a Context
}
impl KeyboardInput<'_> {
pub fn new(_ctx: &Context, left_key: KeyCode, right_key: KeyCode) -> KeyboardInput {
KeyboardInput{
_ctx,
left_key,
right_key
}
}
}
impl Input for KeyboardInput<'_> {
fn get_direction(&self) -> Direction {
if ggez::input::keyboard::is_key_pressed(self._ctx, self.left_key) {
return Direction::Left;
}
if ggez::input::keyboard::is_key_pressed(self._ctx, self.right_key) {
return Direction::Right;
}
Direction::Unchanged
}
}
struct Player {
angle: f32,
pos_x: f32,
pos_y: f32,
input_manager: Box<dyn Input>,
}
impl <'a>MainState {
fn new(ctx: &'a Context) -> GameResult<MainState> {
let kbd_input = KeyboardInput::new(ctx, KeyCode::Left, KeyCode::Right);
let kbd_input = Box::new(kbd_input);
let s = MainState {
last_update: Instant::now(),
players: vec![
Player::new(kbd_input)
]
};
Ok(s)
}
}
pub fn main() -> GameResult {
let cb = ggez::ContextBuilder::new("My game", "ggez");
let (ctx, event_loop) = &mut cb.build()?;
let state = &mut MainState::new(&ctx)?;
event::run(ctx, event_loop, state)
}
and the compiler error:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
--> src\main.rs:75:25
|
75 | let kbd_input = KeyboardInput::new(ctx, KeyCode::Left, KeyCode::Right);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 72:7...
--> src\main.rs:72:7
|
72 | impl <'a>MainState {
| ^^
note: ...so that reference does not outlive borrowed content
--> src\main.rs:75:44
|
75 | let kbd_input = KeyboardInput::new(ctx, KeyCode::Left, KeyCode::Right);
| ^^^
= note: but, the lifetime must be valid for the static lifetime...
= note: ...so that the expression is assignable:
expected std::boxed::Box<(dyn input_manager::Input + 'static)>
found std::boxed::Box<dyn input_manager::Input>
Upvotes: 0
Views: 158
Reputation: 40934
This error almost always means that you are trying to store a value in a way where it can exist longer than it's lifetime.
Let's look at a specific piece of code, annotated with explicit types and lifetimes:
impl<'a> MainState {
fn new(ctx: &'a Context) -> GameResult<MainState> {
let kbd_input: KeyboardInput<'a> = KeyboardInput::new(ctx, KeyCode::Left, KeyCode::Right);
let kbd_input: Box<dyn Input + 'a> = Box::new(kbd_input);
let s: MainState = MainState {
last_update: Instant::now(),
players: vec![
Player::new(kbd_input as Box<dyn Input + 'static>)
]
};
Ok(s)
}
}
On line 9, you're trying to assign kbd_input
to Box<dyn Input>
. But Box<dyn Input>
has no explicit lifetime, it is implicitly equivalent to Box<dyn Input + 'static>
. So you're trying to assign a value with lifetime 'a
to a type with a static lifetime, which is not allowed.
The solution is to explicitly set the lifetime of the trait object type: Box<dyn Input + 'a>
. This cascades and means that you'll need to add a lifetime to the MainState
struct as well, as it now will contain a type with a non-static lifetime:
struct MainState<'a> {
/* ... */
players: Vec<Box<dyn Input + 'a>>,
}
Upvotes: 2