Cannot infer an appropriate lifetime when calling a mutable method with references passed as closure arguments

和自甴很熟 提交于 2019-12-11 04:49:39

问题


I'm trying to make a small game in Rust. I want to use something similar to the entity-component-system pattern to handle all the game objects.

My general idea is to have a GameLoop struct which holds all the necessary data to update and draw the game (the screen, a timestamp, ...).

The World struct is supposed to hold all the game entities and update them in the dispatch function. It calls all the registered callbacks which are stored in the World struct as well (those are the "systems"). They're redundant in the sample code, though.

I've tried to break down the code as much as possible and only include the relevant parts.

use std::marker::PhantomData;

struct Something;

///The "somethings" are things like the display, a timestamp, ...
struct GameLoop {
    sth: Something,
    sth2: Something,
}

///C = Context
///The type that is passed to every entity to give it access to things like the delta time
struct World<C> {
    phantom: PhantomData<C>, //This is here so Rust doesn't complain about the type parameter not being used
}

///The data that is passed to the system functions every frame
struct TickData<'a> {
    delta: u64,
    sth: &'a Something,
    sth2: &'a mut Something,
}

impl GameLoop {
    fn new() -> GameLoop {
        GameLoop {
            sth: Something {},
            sth2: Something {},
        }
    }

    ///One game "tick" - Supposed to do things like calculating delta time, swapping buffers, ...
    ///Those are then passed to the callback
    fn update<F: FnMut(u64, &Something, &mut Something)>(&mut self, f: &mut F) {
        f(0, &self.sth, &mut self.sth2);
    }
}

impl<C> World<C> {
    fn new() -> World<C> {
        World { phantom: PhantomData }
    }

    ///Supposed to update all the game entities
    fn dispatch(&mut self, context: &mut C) {
        //...
    }
}

impl<'a> TickData<'a> {
    fn new<'b>(delta: u64, sth: &'b Something, sth2: &'b mut Something) -> TickData<'b> {
        TickData {
            delta: delta,
            sth: sth,
            sth2: sth2,
        }
    }
}

fn main() {
    let mut game_loop = GameLoop::new();
    let mut world = World::<TickData>::new();

    //The game update function, called once per frame
    let mut update_fnc = |delta: u64, sth: &Something, sth2: &mut Something| {
        let mut tick_data = TickData::new(delta, sth, sth2);

        &world.dispatch(&mut tick_data); //If this line is commented out, it compiles fine

        //...
    };

    loop {
        game_loop.update(&mut update_fnc); //Calculate the delta time, call the specified function and swap buffers
    }
}

There seems to be a problem with borrowing / lifetimes. The compiler is everything else but verbose.

The problem seems to be the &world.dispatch(&mut tick_data) call in the update function of the game, which is supposed to update all the game entities. If I comment it out the program compiles without any errors.

This is what the compiler tells me:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter 'b in function call due to conflicting requirements
  --> src/main.rs:66:29
   |
66 |         let mut tick_data = TickData::new(delta, sth, sth2);
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 65:77...
  --> src/main.rs:65:78
   |
65 |       let mut update_fnc = |delta: u64, sth: &Something, sth2: &mut Something| {
   |  ______________________________________________________________________________^ starting here...
66 | |         let mut tick_data = TickData::new(delta, sth, sth2);
67 | |
68 | |         &world.dispatch(&mut tick_data); //If this line is commented out, it compiles fine
69 | |
70 | |         //...
71 | |     };
   | |_____^ ...ending here
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:66:55
   |
66 |         let mut tick_data = TickData::new(delta, sth, sth2);
   |                                                       ^^^^
note: but, the lifetime must be valid for the expression at 74:25...
  --> src/main.rs:74:26
   |
74 |         game_loop.update(&mut update_fnc); //Calculate the delta time, call the specified function and swap buffers
   |                          ^^^^^^^^^^^^^^^
note: ...so that reference is valid at the time of borrow
  --> src/main.rs:74:26
   |
74 |         game_loop.update(&mut update_fnc); //Calculate the delta time, call the specified function and swap buffers
   |                          ^^^^^^^^^^^^^^^

I simply can't spot the cause of the error. The functions get called in a kind of procedural way and since I'm only borrowing most of the data there should be no problem with the lifetimes.

When I remove the references from the TickData struct so it only contains values that are implemented for the Copy trait, it works as well.

I'm usually not the kind of person to post a wall of code and ask people to fix it but I'm really clueless right now.


回答1:


There's no one right solution to your code. It appears overly complicated and I don't know why you've made some of the design decisions you have. If nothing I say applies, then I apologize and you'll have to wait for the next answerer.


Reducing your problem is the right idea, but why did you stop? It can be reduced all the way down to

struct Something;

struct World<'a> {
    x: TickData<'a>,
}

impl<'a> World<'a> {
    fn dispatch(&mut self, context: &TickData<'a>) {}
}

struct TickData<'a>(&'a Something);

fn update_fnc(world: &mut World, sth: &Something) {
    let tick_data = TickData(sth);
    world.dispatch(&tick_data);
}

fn main() {}

By trial-and-error, one can find some "solutions":

impl<'a> World<'a> {
    fn dispatch(&self, context: &TickData<'a>) {}
}

or

impl<'a> World<'a> {
    fn dispatch(&mut self, context: &TickData) {}
}

or

impl<'a> World<'a> {
    fn dispatch<'b>(&'b mut self, context: &'b TickData<'b>) {}
}

For an extremely thorough analysis of this problem, better than I can give, check out Why does linking lifetimes matter only with mutable references?.


Let's look at another aspect, back at your main method:

let mut world = World::<TickData>::new();

We know that TickData has a lifetime in it, so what is it in this case? We can't specify it like a type, it must be inferred from the usage. So where is it used?

One analogy to look to is a Vec. We create a Vec and then push things onto it later. Those pushes tell us what the concrete type of T is. What does your code do:

let mut world = World::<TickData>::new();
// ...
world.dispatch(&mut tick_data);

You create a type that you've said contains a TickData (that's what PhantomData does), then you call a method that "push"es that type (fn dispatch(&mut self, context: &mut C)), therefore the second argument must be of the type contained, resolving the final type.

This leads to another problem: there's no clue how long the lifetimes of those arguments are.

However, simply annotating the lifetimes isn't enough:

fn update<'a, F>(&'a mut self, mut f: F)
    where F: FnMut(u64, &'a Something, &'a mut Something)
{
    f(0, &self.sth, &mut self.sth2);
}

This further complication is because we are passing the mutable reference sth2 to dispatch. The definition of dispatch allows it to store that mutable reference inside itself - the lifetimes and types match and it's a &mut self.

This could lead to multiple mutable aliases, which is disallowed.

I don't know why you've parameterized your World, but you might be able to just move the C to the dispatch method, removing PhantomData completely. This removes the ability for World to store C.



来源:https://stackoverflow.com/questions/43011827/cannot-infer-an-appropriate-lifetime-when-calling-a-mutable-method-with-referenc

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!