Addressing lifetimes in a basic example of a lib

Hello, everyone :wave: I've just started learning Rust and wanted to understand lifetimes a bit better. The example of my custom iterator, I attached below, works well. But I wanted to also implement the From trait so I could create an instance with .into.


let some_vec = vec![1, 2, 3];
        
// TODO: make the Into imlemenation work
// let mut my_custom_vec: MyVecIterator<i32> = my_vec.into();
let mut my_custom_vec: MyVecIterator<i32> = MyVecIterator::new(&some_vec); 

The full example is below and also on
(a Playground)

trait MyIterator {
    type Item;

    fn next(&mut self) -> Option<Self::Item>;

    fn is_done(&mut self) -> bool;
}

struct MyVecIterator<'a, T> {
    data: &'a Vec<T>,
    current: usize,
}

impl<'a, T> MyVecIterator<'a, T> {
    pub fn new(data: &'a Vec<T>) -> Self {
        Self { data, current: 0 }
    }
}

impl<'a, T> MyIterator for MyVecIterator<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<Self::Item> {
        let current_idx = self.current;
        let current_item = self.data.get(current_idx);

        self.current += 1;

        current_item
    }

    fn is_done(&mut self) -> bool {
        self.current == self.data.len()
    }
}

// TODO: implement lifetimes correctly
// impl<'a, T> From<Vec<T>> for MyVecIterator<'a, T> {
//     fn from (data: Vec<T>) -> Self {
//         Self::new(&data)
//     }
// }

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let some_vec = vec![1, 2, 3];
        
        // TODO: make the Into imlemenation work
        // let mut my_custom_vec: MyVecIterator<i32> = my_vec.into();
        let mut my_custom_vec: MyVecIterator<i32> = MyVecIterator::new(&some_vec);

        assert_eq!(my_custom_vec.is_done(), false);
        assert_eq!(my_custom_vec.next(), Some(&1));
        assert_eq!(my_custom_vec.is_done(), false);
        assert_eq!(my_custom_vec.next(), Some(&2));
        assert_eq!(my_custom_vec.is_done(), false);
        assert_eq!(my_custom_vec.next(), Some(&3));
        assert_eq!(my_custom_vec.is_done(), true);
        assert_eq!(my_custom_vec.next(), None);
    }
}

You can do this:

impl<'a, T> From<&'a Vec<T>> for MyVecIterator<'a, T> {
    fn from(data: &'a Vec<T>) -> Self {
        Self::new(&data)
    }
}

Thank you for such a quick response :raised_hands:

I applied the suggested implementation, and now there's some other error:
Playground link

error[E0277]: the trait bound `MyVecIterator<'_, i32>: From<Vec<{integer}>>` is not satisfied
  --> src/lib.rs:51:62
   |
51 |         let mut my_custom_vec: MyVecIterator<i32> = some_vec.into();
   |                                                              ^^^^ the trait `From<Vec<{integer}>>` is not implemented for `MyVecIterator<'_, i32>`
   |
   = help: the following implementations were found:
             <MyVecIterator<'a, T> as From<&'a Vec<T>>>
   = note: required because of the requirements on the impl of `Into<MyVecIterator<'_, i32>>` for `Vec<{integer}>`

You would have to do (&some_vec).into() to go through the From and Into traits. It's not possible to implement it for Vec<T> directly.

This is why most borrowed iterators are created through an iter method that takes &self, rather than through a conversion trait that takes it by-value.

1 Like

This is excellent! I was not aware of this syntax (&some_vec).

What is the difference between (&some_vec).into() and &some_vec.into()? As a person new to the language, I'd think they are the same. In reality, though, the first one (that you suggested) works perfectly well, while the latter gives an error:

error[E0308]: mismatched types
  --> src/my_iterator.rs:59:51
   |
59 |       let mut my_custom_vec: MyVecIterator<i32> = &some_vec.into();
   |                              ------------------   ^^^^^^^^^^^^^^^^
   |                              |                    |
   |                              |                    expected struct `my_iterator::MyVecIterator`, found reference
   |                              |                    help: consider removing the borrow: `some_vec.into()`
   |                              expected due to this
   |
   = note: expected struct `my_iterator::MyVecIterator<'_, i32>`
           found reference `&_`

It's just about parsing precedence: &some_vec.into() is parsed as &(some_vec.into()) ("call .into() on some_vec, then borrow the returned value"), not (&some_vec).into() ("call .into() on a borrow of some_vec").

1 Like

Understood! Thanks, folks — back to learning :upside_down_face: