Lifetime conflicting requirements

Hi community,

Having trouble understanding following lifetime problem:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=719115e0e89c7189e7060042c3ab2b3e

#[derive(Debug)]
struct Consumer;

impl Consumer {
    fn commit_message(&self, msg: &BorrowedMessage<'_>) {
        println!("commited {:?}", msg);
    }
}

#[derive(Debug)]
struct BorrowedMessage<'a> {
    data: String,
    consumer: &'a Consumer,
}

struct KafkaEventSource {
    consumer: Consumer,
}

impl KafkaEventSource {
    fn new(consumer: Consumer) -> Self {
        Self {
            consumer
        }
    }
} 

#[derive(Debug)]
struct Event {
    data: String,
}

trait EventSource<M> {
    fn recv(&self) -> M;
    
    fn msg_to_event(msg: &M) -> Event;
    
    fn post_hook(&self, msg: &M);
    
    fn run(&self) {
        let msg = self.recv();
        let event = Self::msg_to_event(&msg);
        println!("got event {:?}", event);
        self.post_hook(&msg);
    }
}

impl<'a> EventSource<BorrowedMessage<'a>> for KafkaEventSource {
    fn recv(&self) -> BorrowedMessage<'a> {
        BorrowedMessage { data: "data".to_string(), consumer: &self.consumer }
    }
    
    fn msg_to_event(msg: &BorrowedMessage<'a>) -> Event {
        Event { data: msg.data.clone() }
    }
    
    fn post_hook(&self, msg: &BorrowedMessage<'a>) {
        self.consumer.commit_message(msg);
    }
}

fn main() {
    let consumer = Consumer;
    
    let kafka_event_source = KafkaEventSource::new(consumer);
    
    kafka_event_source.run();
}

   Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
  --> src/main.rs:50:63
   |
50 |         BorrowedMessage { data: "data".to_string(), consumer: &self.consumer }
   |                                                               ^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime defined on the method body at 49:13...
  --> src/main.rs:49:13
   |
49 |     fn recv(&self) -> BorrowedMessage<'a> {
   |             ^^^^^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:50:63
   |
50 |         BorrowedMessage { data: "data".to_string(), consumer: &self.consumer }
   |                                                               ^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 48:6...
  --> src/main.rs:48:6
   |
48 | impl<'a> EventSource<BorrowedMessage<'a>> for KafkaEventSource {
   |      ^^
note: ...so that the expression is assignable
  --> src/main.rs:50:9
   |
50 |         BorrowedMessage { data: "data".to_string(), consumer: &self.consumer }
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: expected `BorrowedMessage<'a>`
              found `BorrowedMessage<'_>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0495`.
error: could not compile `playground`

To learn more, run the command again with --verbose.

First look at the definition of EventSource, with lifetimes un-elided:

trait EventSource<M> {
    fn recv<'t>(&'t self) -> M;
    // ...
}

There is no mention of 't in the return type of recv, so an implementation of recv is not allowed to return data that borrows from the receiver (meaning self).

Later, you try to implement EventSource<BorrowedMessage<'a>> for KafkaEventSource like this:

impl<'a> EventSource<BorrowedMessage<'a>> for KafkaEventSource {
    fn recv(&self) -> BorrowedMessage<'a> {
        BorrowedMessage {
            data: "data".to_string(),
            consumer: &self.consumer,
        }
    }

    // ...
}

You're trying to return a value that borrows from self—that's what the expression &self.consumer implies—but this is not allowed by the signature of recv, so you get an error.

If you want recv to be able to return data that borrows from the receiver, you'd need a signature like this:

trait EventSource {
    type Message<'ev>;
    fn recv(&self) -> Self::Message<'_>;
    // ...
}

…but that's not possible (on stable Rust) until we get generic associated types. Instead, have you considered changing BorrowedMessage so that it doesn't hold a reference, i.e. so it doesn't borrow anymore? You could probably replace consumer: &'a Consumer with consumer: Arc<Consumer>. Or if borrowing from the receiver is essential, you could try to get rid of the generic parameter M to EventSource and have recv return a specific concrete type Message<'_> instead.

4 Likes

Here I'll address understanding the error message, but not possible solutions. In case you haven't dealt with it much, you can read 'a: 'b as "'a outlives 'b" or "'a is valid at least wherever 'b is valid".

This is saying, "To implement EventSource<M>, you have to be able to produce an M from a &'borrow Self, no matter how long or short the 'borrow is."

So declaring impl<'msg> EventSource<BorrowedMessage<'msg>> for KafkaEventSource is saying, "For any chosen lifetime 'msg, I can produce a BorrowedMessage<'msg> from any &'borrrow KafkaEventSource."

But the body of your method is returning something borrowed from &self -- its creating a BorrowedMessage<'x> where 'borrow: 'x. (It's impossible for 'x to be valid anywhere 'borrow isn't valid.)

So you can't keep the promise of the implementation unless 'borrow: 'msg.

There's no way to limit your implementation in such a way for the reasons that @cole-miller explained: the contract of EventStore<M> is such that you can't put any restrictions on 'borrow.

Also perhaps consider what would have to happen here:

let local = Consumer;
let kes = KafkaEventSource::new(local);
let borrow /* : BorrowedMessage<'static> */ = 
  <KafkaEventSource as EventSource<BorrowedMessage<'static>>::recv(&kes);
let consume: &'static Consumer = borrow.consumer;
//            ^^^^^^^ !

(The trait as defined allows this.)

4 Likes

As long as it’s about lifetimes only, they can be replaced with ordinary traits and HRTBs easily. All you’ll need is

trait HasMessageType<'a> {
    type Message;
}

perhaps throw in an extra parameter in the mix to make Message only defined when Self: 'a:

trait HasMessageType<'a, _Outlives = &'a Self> {
    type Message;
}

Add it as a supertrait

trait EventSource: for<'a> HasMessageType<'a>

and perhaps use a convenience type synonym

type Message<'a, This> = <This as HasMessageType<'a>>::Message;

and use it


trait EventSource: for<'a> HasMessageType<'a> {
    fn recv(&self) -> Message<'_, Self>;

    fn msg_to_event(msg: &Message<'_, Self>) -> Event;

    fn post_hook(&self, msg: &Message<'_, Self>);

    fn run(&self) {
        let msg = self.recv();
        let event = Self::msg_to_event(&msg);
        println!("got event {:?}", event);
        self.post_hook(&msg);
    }
}

Here’s the full code in the playground.

6 Likes

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.