I've recently started playing with async streams in Rust, and I keep finding myself in situations where I want to use async functions in the implementation of a Stream. The async functions often come from libraries I don't control, but for the sake of example suppose they look like this:
async fn bar_str(s: &str) -> String {
s.to_string()
}
async fn bar_string(s: String) -> String {
s
}
Also to keep things simple suppose I'm just trying to use these functions to implement a trait like the following (no actual stream stuff involved):
use std::future::Future;
trait Foo {
fn bar(self) -> Box<dyn Future<Output = String>>;
}
For the String case this just works as you'd expect:
impl Foo for String {
fn bar(self) -> Box<dyn Future<Output = String>> {
Box::new(bar_string(self))
}
}
For the case where the async function borrows, it doesn't.
impl Foo for &str {
fn bar(self) -> Box<Future<Output = String>> {
Box::new(bar_str(self))
}
}
This fails to compile:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter '_ in function call due to conflicting requirements
--> foo.rs:23:18
|
23 | Box::new(bar_str(self))
| ^^^^^^^^^^^^^
|
...
I can understand why this is a problem, and I understand that the async fn syntax provides special handling for borrowed arguments like this (although I don't know anything about how it's actually checked, desugared, etc.).
My question is about what the best thing is to do in these situations generally. Is there some way I can reproduce the magic async fn is doing in my non-async fn code? Should I just avoid borrowing in async functions (when I can, since that's often a decision I didn't make)? In the code I'm currently writing I'm happy to use experimental or not-necessarily-future-proof solutions if they make reading and writing this kind of thing nicer.