Reference does not live long enough error

Hey, I'm trying to implement a state machine, and I would like to pass a generic struct of data to its "update()" function that might potentially have mutable and const references, and I keep getting various kinds of lifetime issues. I'm new to rust, so I have very superficial understanding of lifetimes, and I find it hard to reason about what happens. Here's the snippet of code I was playing around with:


pub trait State<T, C> {
    fn on_enter(&mut self, _persistent_data: &mut T) {}
    fn on_exit(&mut self, _persistent_data: &mut T) {}
    fn update(&mut self, persistent_data: &mut T, context: &mut C) -> UpdateResult<T, C>;
}

pub enum UpdateResult<T, C> {
    KeepUpdating,
    Transition(Box<dyn State<T, C>>),
    Stop
}

pub struct StateMachine<T, C> {
    persistent_data: T,
    active_state: Option<Box<dyn State<T, C>>>,
}

impl<T, C> StateMachine<T, C> {
    pub fn new(persistent_data: T) -> Self {
        Self {
            persistent_data,
            active_state: None,
        }
    }

    pub fn push(&mut self, state: Box<dyn State<T, C>>) {
        self.pop();
        self.active_state = Some(state);
        self.active_state.as_deref_mut().unwrap().on_enter(&mut self.persistent_data);
    }

    pub fn pop(&mut self) {
        if let Some(state) = self.active_state.as_deref_mut() {
            state.on_exit(&mut self.persistent_data);
            self.active_state = None;
        }
    }

    pub fn update(&mut self, context: &mut C) {
        if let Some(state) = self.active_state.as_deref_mut() {
            match state.update(&mut self.persistent_data, context) {
                UpdateResult::Transition(new_state) => {
                    self.push(new_state);
                    self.update(context);
                },
                UpdateResult::Stop => self.active_state = None,
                UpdateResult::KeepUpdating => ()
            }
        }
    }
}

struct PersistentData {
}

struct DataAccessorState<C> {
    phantom_data: std::marker::PhantomData<C>
}

impl<C> DataAccessorState<C> {
    fn new() -> Self {
        Self {
            phantom_data: std::marker::PhantomData
        }
    }
}

struct Data {
}

trait DataRefTrait {
    fn get_data(&self) -> &Data;
}

struct DataRefTraitImpl<'a> {
    data_ref: &'a Data
}

impl<'a> DataRefTrait for DataRefTraitImpl<'a> {
    fn get_data(&self) -> &Data {
        self.data_ref
    }
}

impl<C: DataRefTrait> State<PersistentData, C> for DataAccessorState<C> {
    fn update(&mut self, _persistent_data: &mut PersistentData, context: &mut C) -> UpdateResult<PersistentData, C> {
        let _data = context.get_data();
        UpdateResult::Stop
    }
}

fn main() {
    let mut state_machine = StateMachine::new(PersistentData {});
    state_machine.push(Box::new(DataAccessorState::new()));
    
    let data = Data {};
    state_machine.update(&mut DataRefTraitImpl { data_ref: &data });
}

(Playground)

Could someone help me make sense of the errors I'm getting, and prod me in the direction on how to handle this?

If you want the contents of a mutable object (StateMachine) to be able to contain references to some data, you have to ensure all the data outlives the machine. Then you can add a lifetime parameter and declare the dyn to allow what is currently rejected due to implicit 'static:

pub struct StateMachine<'a, T, C> {
    persistent_data: T,
    active_state: Option<Box<dyn State<T, C> + 'a>>,
}

It's not possible without unsafe code for the state machine to contain shorter-lived references, and if you do seek to use unsafe code for the purpose, you'll need to provide an API that ensures that the state doesn't outlive the lifetime of the borrowed data. (See std::thread::scope for an example of how that can be enforced.) That requirement might conflict with your goals but it is necessary for soundness.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.