Why does rust want a lifetime bound on this type parameter?

This code does not compile. Rust wants me to add a bound F: 'house on the second function (scroll down to see it).

#[derive(Clone)]
pub struct Book {
    // ...some information...
}

pub struct Room {
    // ...some furniture...
}

pub fn search_fallible<'house, F, Iter, Err>(
    rooms: &'house [Room],
    search_room: F,
) -> Result<impl Iterator<Item = Book> + 'house, Err>
where
    F: FnMut(&'house Room) -> Result<Iter, Err>,
    Iter: Iterator<Item = Book> + 'house,
{
    let results = rooms
        .iter()
        .map(search_room)
        .collect::<Result<Vec<_>, Err>>();
    // At this point `search_room` has been dropped.
    results.map(|lists| lists.into_iter().flatten())
}

/// The type of an error that never happens.
enum Never {}

pub fn search<'house, F, Iter>(
    rooms: &'house [Room],
    mut search_room: F,
) -> impl Iterator<Item = Book> + 'house
where
    F: FnMut(&'house Room) -> Iter,
    Iter: Iterator<Item = Book> + 'house,
{
    let result = search_fallible(rooms, move |room| -> Result<_, Never> {
        Ok(search_room(room))
    });
    // At this point `search_room` has been dropped.
    result.unwrap_or_else(|err| match err {})
}

Looking at the code itself, it seems unnecessary. Both functions drop the F value before returning.

Somehow the type of search_fallible embeds some invisible connection between F and 'house that I don't want. What's the best way to get rid of it?

When you have a callback, you almost always want it in the form:

for<'a> FnMut(&'a Room)

otherwise it means the parameter for the function needs to exist and be valid for way longer than the function, and had to be created even before search was called, and outlive that whole outer call.

When a function gets a reference with 'house it doesn't matter that itself is dropped. The element has been given to the function for the whole 'house lifetime, so the function is allowed to keep it beyond its own lifetime (e.g. store it in a shared/global container also bound by 'house).

1 Like

Hmm. That doesn't fix it.

Anyway, "almost always" is too strong; this case is a valuable exception to that rule. Here, I think using for<'a> FnMut(&'a) would make both search functions impossible to use successfully.

The idea is: The closure is short-lived. It creates iterators that can live as long as the underlying data ('house).

1 Like

In general this is correct (and the bound can just be written as F: FnMut(&Room), which implicitly inserts the HRTB), but it's not sufficient to solve the issue here: playground.


I think the problem is that the compiler thinks of the return type of search_fallible as an opaque type that has all the generics of search_fallible, that is, <'house, F, Iter, Err>, and requiring this type to be : 'house requires F: 'house.

This version uses the nightly-only type-alias-impl-trait feature to remove F from the generics of the return type, and it compiles. I'm not sure if there is a good workaround for this on stable, other than just not using impl Trait and naming the type like so.

1 Like

Github issue:

https://github.com/rust-lang/rust/issues/42940

2 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.