Can't infer lifetime due to conflicting requirements when returning a stream

Hi! I have the following simplified version of my code which in here doesn't make much sense but exemplifies the same problem:

Code
#![feature(async_closure)]

use futures::{StreamExt, FutureExt};
use futures::stream::BoxStream;
use std::sync::{Arc, RwLock};
use async_trait::async_trait;

#[async_trait]
pub trait Getter: Send + Sync
{
   async fn get(&self) -> String;
   fn stream(&self) -> BoxStream<()>;
}

struct Baz(String);

#[async_trait]
impl Getter for Baz {
   async fn get(&self) -> String {
      self.0.clone()
   }

   fn stream(&self) -> BoxStream<()> {
      async { () }.into_stream().boxed()
   }
}

struct Bar {
   pub getter: Arc<RwLock<dyn Getter>>
}

impl Bar {
   pub fn foo(&mut self) -> BoxStream<String> {
      let getter = self.getter.clone();

      let stream = { getter.read().unwrap().stream() };

      let final_stream = stream
         .map(move |_| getter.read().unwrap().get().into_stream())
         .flatten();

      final_stream.boxed()
   }
}

fn main() {
   use async_std::task;

   let baz = Arc::new(RwLock::new(Baz("baz!".to_string())));
   
   let mut bar = Bar{ getter: baz };

   task::block_on(async move { bar.foo().next().await });
}

For which I am getting the following error:

Compiler error output
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
  --> src\main.rs:39:31
   |
39 |          .map(move |_| getter.read().unwrap().get().into_stream())
   |                               ^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'_` as defined on the body at 39:15...
  --> src\main.rs:39:15
   |
39 |          .map(move |_| getter.read().unwrap().get().into_stream())
   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that closure can access `getter`
  --> src\main.rs:39:24
   |
39 |          .map(move |_| getter.read().unwrap().get().into_stream())
   |                        ^^^^^^
note: but, the lifetime must be valid for the method call at 38:26...
  --> src\main.rs:38:26
   |
38 |         let final_stream = stream
   |  __________________________^
39 | |          .map(move |_| getter.read().unwrap().get().into_stream())
   | |__________________________________________________________________^
note: ...so that a type/lifetime parameter is in scope here
  --> src\main.rs:38:26
   |
38 |         let final_stream = stream
   |  __________________________^
39 | |          .map(move |_| getter.read().unwrap().get().into_stream())
   | |__________________________________________________________________^

error: aborting due to previous error

But I don't understand the explanation given by the compiler.

How can I interpret the explanation given by the compiler and what possible fixes can I apply?

Lifetimes are used to mark where the data is borrowed from.

In the case of:

fn stream<'a>(&self) -> BoxStream<'a, ()>;

The 'a doesn't point to anything. Where is the data source for 'a? What is this method borrowing?

This method says the data being returned in BoxStream is definitely not from self, because the lifetime doesn't point to self. So there's no way to create BoxStream with data other than compile-time constant or leaked memory ('static), because Rust has no other way of making a reference out of thin air.

Yes, true, forgot to delete that from my real code (just edited my OP). Nonetheless the problem is still there because the lifetime requirement is for the read() method.

Hmm, I got to this point:

where getter is valid, getter.clone() should also be. Making it this way narrows the scope of the errors, and makes it clearer what Rust really doesn't like here.

Thank you kornel for your time and example, let me check it out!

Oh! I know. That's a quirk of async fn.

async fn does not run when you call it. Instead, calling it merely puts function's arguments in a Future to be called later when polled. This means &self is always put in the Future, even when it's not needed.

Such code:

async fn get(&'a self) -> String {…}

Is roughly equivalent to this:

fn get<'a>(&'a self) -> impl Future<Output=String> + 'a {…}

If you write it as:

fn get(&self) -> impl Future<Output=String> {
   let tmp = self.0.clone()
   async move {
      tmp
   }
}

then it will run the clone immediately, and won't need &self later.

But how you express it with impl syntax inside a trait? I would have to use boxing right?

I think so. AFAIK #[async_trait] is a syntax sugar for boxing too.

Sad but true. Let me try that alternative you posted and see if that works.

Tested it here but still the same error you had in your playground example. Is that expected?

fn get(&self) -> BoxFuture<'_, String> tells Rust to use default rules for lifetimes '_, and the default Rule is that the BoxFuture borrows from &self, and therefore is forbidden from being used after &self is gone.

To say that something is not tied to a single temporary scope, you must call its lifetime 'static, i.e. BoxFuture<'static, String>

So that means that for this code I can only ever use a static lifetime?

Yes. You can't give out "unlocked" references to data under a lock, because that bypasses the lock.

You need to keep in mind that async code doesn't execute in the order its written, in the scope it's called. It can be polled sometime later, somewhere else, after all your temporary references are gone, and the locks are locked or in someone else's control.

'static in Rust is an oddball, it doesn't really mean you should use 'static references (they're useless for most purposes), it's only a way to forbid temporary references, and require use of Arc or other owned types.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.