"cannot infer an appropriate lifetime" for saving internal object


#1

Help me, Rustaceans, you’re my only hope.

I’m using a couple of crates: one provides a session and listener concept, the other provides an event loop via a client-implemented perform() trait. I’m trying to implement a simple perform() which creates a listener during an early call, and uses that listener for all subsequent calls, but am having trouble reconciling the lifetimes of the objects involved. Here’s the stripped-down version of the crates and my own code:

// ----- crate 1

struct Session;

impl Session {
  pub fn listen(&mut self) -> Listener {
    return Listener {session: self};
  }
}

struct Listener<'session> {
  session: &'session mut Session,
  // other useful things
}


// ----- crate 2

trait Perform {
  fn perform(&mut self);
}

struct Looper;

impl Looper {
  pub fn main_loop<P: Perform>(&mut self, p: P) {
    // calls p.perform() a bunch of times
  }
}


// ----- self

struct MyPerform<'session> {
  session: Session,
  listener: Option<Listener<'session>>,
}

impl<'session> Perform for MyPerform<'session> {
  fn perform(&mut self) {
    match self.listener {
      None => {
        self.listener = Some(self.session.listen());
        // the problem is here:           ^~~~~~~~
      }
      Some(e) => {
        // use self.listener, I no longer care about self.session
      }
    }
  }
}

fn main() {
  let session = Session;
  let perform = MyPerform {session: session, listener: None};

  let mut looper = Looper;
  looper.main_loop(perform);
}

When I try to compile this (rustc 1.5.0), I get 43:43 43:51 error: cannot infer an appropriate lifetime for autoref due to conflicting requirements. I think I understand what the problem is: the returned listener must simultaneously have (elided) lifetime 'a and 'session, which don’t overlap in any predictable way. Maybe?

It seems like this should be possible, though: my struct has exclusive access to both session and listener, so it should be able to guarantee proper access to them. But every attempt to get this to work fails at compile time (which, I guess, is better than failing at runtime).

So, is what I’m trying to do possible, and if so, what’s the best way? I’d prefer to use safe code, and not edit imported crates. I’m a Rust noob, so feel free to critique my design, if that’s my problem.


#2

The issue is that structs cannot have references into themselves. So you cannot have MyPerform::listener point to the Session that is stored in the MyPerform. Because if you’d move the MyPerform object into another variable and thus another memory location, that reference would now point to dangling memory.


#3

Yes, it seems that is the issue. Unfortunately, the structure of Listener is defined in another crate. Does this mean that I can’t use these two crates together as I want? Is there a way to pinky-swear to rustc that I won’t move an object? :wink: The linear code

let mut session = Session;
let mut listener = session.listen();
// use listener, ignore session

Obviously works fine, it seems like there should be a way to capture this in handler code somehow. Anyway, thanks, I’ll keep toying with it.


#4

For those who care, I solved the problem by going through the crates, and realizing that Listener's ref to Session was actually immutable. So I changed the session type in MyPerform from Session to &'session Session, and so it (potentially) has two borrows to session: one via session, and one via listener -> session. But they’re both immutable, so it works as long as in main, perform lives no longer than session, which I think I can live with. Final trivial code:

// ----- crate 1

struct Session;

impl Session {
  pub fn listen(&self) -> Listener {
    return Listener {session: self};
  }
}

struct Listener<'session> {
  session: &'session Session,
  // other useful things
}


// ----- crate 2

trait Perform {
  fn perform(&mut self);
}

struct Looper;

impl Looper {
  pub fn main_loop<P: Perform>(&mut self, mut p: P) {
    p.perform();
    p.perform();
  }
}


// ----- self

struct MyPerform<'session> {
  session: &'session Session,
  listener: Option<Listener<'session>>,
}

impl<'session> Perform for MyPerform<'session> {
  fn perform(&mut self) {
    match self.listener {
      None => {
        println!("Using session.");
        self.listener = Some(self.session.listen());
      }
      Some(ref l) => {
        println!("Using listener.");
        // use self.listener, I no longer care about self.session
      }
    }
  }
}

fn main() {
  let session = Session;
  let perform = MyPerform {session: &session, listener: None};

  let mut looper = Looper;
  looper.main_loop(perform);
}

Obviously, I’m still open to critiques of this design. Let me know, and thanks for your help.