rust-lang / project-error-handling

Error handling project group
Apache License 2.0
268 stars 18 forks source link

Better compiler support for adding lifetime specifiers when returning iterators #54

Open helanhalvan opened 1 year ago

helanhalvan commented 1 year ago

Hi,

I'm not sure if this is the right forum, if not, I'm sorry. Anyway, I had some trouble with lifetime specifiers where the compile errors are a bit strange.

The original code looks like this:

pub fn pos_iter_to_cells(
    pos: impl IntoIterator<Item = Pos>,
    m: &Board,
) -> Vec<Option<(Pos, celldata::CellState)>> {
    let ret = pos
        .into_iter()
        .map(|p @ Pos { x, y }| match m.get(x) {
            Some(v) => match v.get(y) {
                None => None,
                Some(&a) => Some((p, a)),
            },
            None => None,
        })
        .collect();
    return ret;
}

The idea is to take an iterator of positions and return and iterator of positions and their contents, however I couldn't figure out what went wrong when returning an iterator. Just removing the collect and changing the return type leads to:

error[[E0700]](https://doc.rust-lang.org/stable/error_codes/E0700.html): hidden type for `impl IntoIterator<Item = Option<(Pos, CellState)>>` captures lifetime that does not appear in bounds

and recommends adding a lifetime specifier + '_, that addition in turn leads to this error:

error[[E0311]](https://doc.rust-lang.org/stable/error_codes/E0311.html): the associated type `<impl IntoIterator<Item = Pos> as IntoIterator>::IntoIter` may not live long enough

Now, the solution is to ensure pos and m live for at least as long as the return value, which is done by adding lifetime specifiers on the arguments and return value like this:

pub fn pos_iter_to_cells<'a>(
    pos: impl IntoIterator<Item = Pos> + 'a,
    m: &'a Board,
) -> impl IntoIterator<Item = Option<(Pos, CellState)>> + 'a {
    let ret = pos.into_iter().map(|p @ Pos { x, y }| match m.get(x) {
        Some(v) => match v.get(y) {
            None => None,
            Some(&a) => Some((p, a)),
        },
        None => None,
    });
    return ret;
}

However getting there for me required going and asking reddit. It might be that I missed some documentation, not sure. Anyway made a rust-lang-gist with all the types required. Here is the discussion on reddit