rust - 无法定义适当的防 rust 生命周期要求

标签 rust traits lifetime

TL; DR
嵌套对象的生存期存在问题。代码如下。

长版:

我正在使用ggez编写多人游戏,并且试图为输入创建一个抽象层(以允许本地和远程玩家一起玩)。

为了做到这一点,我创建了一个Input特性,并为本地输入实现了KeyboardInput,它使用ggez键盘状态查询方法。

现在最棘手的部分:ggez在启动时创建Context对象,并期望在大多数公开的函数中对其进行引用。

因为我的KeyboardInput实现使用的是ggez输入法(特别是is_key_pressed),所以必须将&Context传递给此方法。但是,由于特征本身应该是通用的,因此任何其他实现(例如Context)都不需要NetworkInput引用。

我的解决方案是在Context结构中添加对KeyboardInput的引用作为字段。但是,这导致了我仍然无法解决的终身错误。

我还尝试将生命周期设置为'static,但这也不起作用。

这是相关的代码:

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)
}


和编译器错误:
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>

最佳答案

该错误几乎总是意味着您试图以一种比其生命周期更长的时间存储值。

让我们看一段特定的代码,这些代码带有显式的类型和生存期:

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)
    }
}

在第9行上,您尝试将kbd_input分配给Box<dyn Input>。但是Box<dyn Input>没有明确的生存期,它隐式等效于Box<dyn Input + 'static>。因此,您试图将带有生存期'a的值分配给具有静态生存期的类型,这是不允许的。

解决方案是显式设置特征对象类型的生存期:Box<dyn Input + 'a>。这是级联的,这意味着您还需要向MainState结构添加生命周期,因为它现在将包含具有非静态生命周期的类型:

struct MainState<'a> {
  /* ... */
  players: Vec<Box<dyn Input + 'a>>,
}

关于rust - 无法定义适当的防 rust 生命周期要求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59491563/

相关文章:

rust - 如何将生命周期限制在代码区域?

asp.net - ASP.Net 中的 JWT token 异常(生命周期验证失败。 token 缺少过期时间。)

rust - 如何使用迭代器将对象映射反转为元组向量?

c++ - 从 trait 获取 const 或非常量引用类型

rust - 在 Rust 中实现 Fn(&something) 的特征

generics - 编写具有关联类型作为参数的递归特征方法时出错

vector - 为什么 borrow checker 提示这些不同切片的生命周期?

rust - 如何在以 WASM 为目标的 near-sdk Rust 代码中链接 WASM 二进制文件

reflection - 在 Rust 中是否有来自 C# 的 nameof() 的模拟?

rust - Rust 泛型上的类型参数不匹配错误