Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return future with lifetime that's different from &selfs #37

Closed
cpick opened this issue Oct 3, 2019 · 3 comments
Closed

Return future with lifetime that's different from &selfs #37

cpick opened this issue Oct 3, 2019 · 3 comments
Labels

Comments

@cpick
Copy link

cpick commented Oct 3, 2019

I have a Stream that needs to run a Future from a trait method on each iteration. I've managed to get it to work using a LocaslBoxFuture:

pub trait Make {
    // working
    fn make<'a, T: Default + 'a>(&self) -> LocalBoxFuture<'a, T> {
        Box::pin(future::ready(T::default()))
    }
}

struct Foo {}
impl Make for Foo {}

fn main() {
    let f = Foo {};
    block_on_stream(stream::empty().map(move |_: ()| f.make::<bool>()));
}

However, I haven't been able to figure out how to accomplish the same with async_trait. For example, attempting the following fails:

#[async_trait]
pub trait Make {
    // broken
    async fn make<'a, T: Default + 'a>(&self) -> T {
        T::default()
    }
}

With error:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/main.rs:20:56
   |
20 |     block_on_stream(stream::empty().map(move |_: ()| f.make::<bool>()));
   |                                                        ^^^^
   |
note: first, the lifetime cannot outlive the lifetime '_ as defined on the body at 20:41...
  --> src/main.rs:20:41
   |
20 |     block_on_stream(stream::empty().map(move |_: ()| f.make::<bool>()));
   |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that closure can access `f`
  --> src/main.rs:20:54
   |
20 |     block_on_stream(stream::empty().map(move |_: ()| f.make::<bool>()));
   |                                                      ^
note: but, the lifetime must be valid for the method call at 20:21...
  --> src/main.rs:20:21
   |
20 |     block_on_stream(stream::empty().map(move |_: ()| f.make::<bool>()));
   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that a type/lifetime parameter is in scope here
  --> src/main.rs:20:21
   |
20 |     block_on_stream(stream::empty().map(move |_: ()| f.make::<bool>()));
   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

error: could not compile `stream-async-trait`.

I've tried various permutations with 'async_trait and 'a leading to similar results.

I'm trying to figure out if what I'm attempting is supported and I'm just missing something?
Perhaps this is related to #8 (I don't have quite a strong enough handle on Futures/lifetimes to tell for sure)?

@dtolnay
Copy link
Owner

dtolnay commented Oct 4, 2019

fn make<'a, T: Default + 'a>(&self) -> LocalBoxFuture<'a, T> {

This signature is not expressible using async fn in Rust (whether or not async_trait is involved). An async fn in Rust does not begin executing until the first time it is awaited, which means every input lifetime is necessarily captured by the future.

@cpick
Copy link
Author

cpick commented Oct 4, 2019

Ahhh, that's interesting and makes sense.

So it sounds like while it would be technically possible for async_trait to support this (because of its current implementation), such support wouldn't really be in keeping with the intent of this crate (which I assume is to act just as as closely to how async trait members will work if they're ever implemented upstream)?

If that's true, I think this issue should be closed, thank you.

I realize this is now just a general async rust question, so of course feel free to just redirect me to StackOverflow :), but I was wondering if there was a common/recommended pattern to accomplish what I'm after. I'm thinking that I'll do the following and just accept that LocalBoxFuture will be with me for the duration:

fn make<'a, T: Default + 'a>(&self) -> LocalBoxFuture<'a, T> {
    // do all work with `self` here

    Box::pin(async {
        // use nice `?` error-handling here
        T::default()
    })
}

@dtolnay
Copy link
Owner

dtolnay commented Oct 4, 2019

Your approach seems fine to me. Alternatively:

fn make<T: Default>(&self) -> impl Future<Output = T> {
    // using `self`

    async {
        T::default()
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants