Lifetimes in T: IntoIterator<Item = &'a Leaf>

Hi folks,

I implemented for IntoIterator for a &Tree only (I didn't implement it for Tree). The reason is simply that I don't need it and since the elements are stored in a boxed slice, the consuming iterator would need to copy the elements and I don't need that.

One thing I'm confused is how to set lifetimes.

I think the following would be wrong:

T: Clone + Default + IntoIterator<Item = &'a Leaf>

and it should be:

T: Clone + Default + 'a,
&'a T: IntoIterator<Item = &'a Leaf>,

Could you please tell me if that's correct?

All code:

use std::{slice, vec};

#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct Leaf {
    name: String,
}
impl Leaf {
    fn new(name: String) -> Self {
        Self { name }
    }
    fn name(&self) -> &str {
        self.name.as_ref()
    }
}

#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct Tree {
    leafs: Box<[Leaf]>,
}
impl Tree {
    fn new(leafs: Vec<Leaf>) -> Self {
        Self {
            leafs: leafs.into_boxed_slice(),
        }
    }

    fn iter(&self) -> slice::Iter<'_, Leaf> {
        self.leafs.iter()
    }
}
impl<'a> IntoIterator for &'a Tree {
    type Item = &'a Leaf;
    type IntoIter = slice::Iter<'a, Leaf>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct Forest;
impl Forest {
    fn get_leafs<'a, T>(tree: &'a T) -> Vec<String>
    where
        T: Clone + Default + 'a,
        &'a T: IntoIterator<Item = &'a Leaf>,
        // T: Clone + Default + IntoIterator<Item = &'a Leaf>
    {
        tree.into_iter().map(|x| x.name().to_owned()).collect()
    }
}

#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct BigForest;
impl BigForest {
    fn get_leafs<'a, T>(trees: &'a [T]) -> Option<Vec<String>>
    where
        T: Clone + Default + 'a,
        &'a T: IntoIterator<Item = &'a Leaf>,
        // T: Clone + Default + IntoIterator<Item = &'a Leaf>
    {
        match trees {
            &[] => None,
            &[ref first_tree, ..] => Some(Forest::get_leafs(first_tree)),
        }
    }
}

fn main() {
    let tree = Tree::new(vec![
        Leaf::new("first".to_owned()),
        Leaf::new("second".to_owned()),
        Leaf::new("third".to_owned()),
    ]);
    let another_tree = Tree::new(vec![
        Leaf::new("first".to_owned()),
        Leaf::new("second".to_owned()),
        Leaf::new("third".to_owned()),
    ]);
    let x: Vec<_> = tree.into_iter().map(|leaf| leaf.name()).collect();
    println!("{:?}", x);

    let x = Forest::get_leafs(&tree);
    println!("{:?}", x);

    let x = BigForest::get_leafs(&[tree, another_tree]);
    println!("{:?}", x);
}

Lifetime of the iterator doesn't have to be as long as the lifetime of the elements in it, e.g. drop(vec.iter()); drop(vec.iter()) is perfectly valid.

So &'a Iter<Item=&'a> is not necessary.

In generic contexts, T can be any type, including a reference. It's valid for T to be &Tree.

You're not keeping or returning T in this function, so you don't even care what lifetime it has (it always has lifetime long enough to be used in the function body).

    fn get_leafs<'a, T>(tree: T) -> Vec<String>
    where
        T: IntoIterator<Item = &'a Leaf>,
    {
        // T is &Tree
        tree.into_iter().map(|x| x.name().to_owned()).collect()
    }
3 Likes

Thank you very much @kornel , it makes sense!

The only problem after I changed it is:

#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct Forest;
impl Forest {
    fn get_leafs<'a, T>(tree: T) -> Vec<String>
    where
        T: Clone + IntoIterator<Item = &'a Leaf>,
    {
        tree.into_iter().map(|x| x.name().to_owned()).collect()
    }
}

#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct BigForest;
impl BigForest {
    fn get_leafs<'a, T>(trees: &'a [T]) -> Option<Vec<String>>
    where
        T: Clone + IntoIterator<Item = &'a Leaf>,
    {
        match trees {
            &[] => None,
            &[ref first_tree, ..] => Some(Forest::get_leafs(first_tree)),
            /////////////////////////////////////////////   ^^^^^^^^^^ `&T` is not an iterator
        }
    }
}

Adjust your bounds:

    fn get_leafs<'a, T>(trees: &'a [T]) -> Option<Vec<String>>
    where
        T: Clone,
        &'a T: IntoIterator<Item = &'a Leaf>, // <---
1 Like

In the second case you will only be able to get &T out of &[T], because slices themselves are borrowed, and here you're referring to the not-yet-borrowed type inside the slice.

1 Like

Thank you very much @kornel and @quinedot , that confused me since I assumed the trait bound should look the same. Thanks!