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

Function parameter lifetime in StreamExt::filter generic isn't good enough #2464

Open
ShadowJonathan opened this issue Jul 16, 2021 · 2 comments
Labels
A-stream Area: futures::stream bug

Comments

@ShadowJonathan
Copy link

I have this following call on a stream, to filter out elements asynchronously (sorry for the ugly encapsulation, async closures aren't a thing yet);

.filter(move |user_id| Box::pin(async move {!db.users.is_deactivated(user_id).await.unwrap_or(false)}))

The important part here is that .is_deactivated takes a borrowed UserId type, which is taken across await boundaries with the subsequent .await

However, rust complains about this...

error: lifetime may not live long enough
   --> src/database/rooms.rs:802:36
    |
802 |             .filter(move |user_id| Box::pin(async move {!db.users.is_deactivated(user_id).await.unwrap_or(false)}))
    |                           -------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
    |                           |      |
    |                           |      return type of closure `Pin<Box<impl futures::Future>>` contains a lifetime `'2`
    |                           has type `&'1 ruma::UserId`

...because the filter function signature doesn't explicitly specify the borrow lifetime as OK to be used for a little while longer;

    fn filter<Fut, F>(self, f: F) -> Filter<Self, Fut, F>
    where
        F: FnMut(&Self::Item) -> Fut,
        Fut: Future<Output = bool>,
        Self: Sized,
    {
        assert_stream::<Self::Item, _>(Filter::new(self, f))
    }

I think that altering it to this fixes it, but i'm not sure how that counts with API cleanliness and lifetime soundness;

    fn filter<'a, Fut, F>(self, f: F) -> Filter<Self, Fut, F>
    where
        F: FnMut(&'a Self::Item) -> Fut,
        Fut: Future<Output = bool> + 'a,
        Self::Item: 'a,
        Self: Sized,
    {
        assert_stream::<Self::Item, _>(Filter::new(self, f))
    }

Quickly inserting this seems to pop up a lot of other issues, so i'm only going to report this issue for now here.

@taiki-e taiki-e added A-stream Area: futures::stream bug labels Aug 6, 2021
@taiki-e
Copy link
Member

taiki-e commented Aug 7, 2021

related: #2046

@rami3l
Copy link
Member

rami3l commented Dec 10, 2022

Just coming by to leave a friendly note:

As a workaround, .filter_map() + .then_some() might work when .filter() doesn't.

For a more concrete example, see line 19 of this demo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-stream Area: futures::stream bug
Projects
None yet
Development

No branches or pull requests

3 participants