Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly implement Iterable structure in Rust? [duplicate]

Tags:

iterator

rust

I'm trying to implement a structure that can be infinitely iterated. Think it like a natural number. I have a limitation: it can't implement Copy trait because the structure contains a String field.

I've also implemented an Iterable trait and its only member fn next(&mut self) -> Option<Self::Item>.

Currently, I have the following code to iterate over the first 10 items of my structure:

let mut counter = 0;
let mut game:Option<Game> = Game::new(&param);
loop {
    println!("{:?}", game); 

    game = g.next();
    counter = counter + 1;
    if counter > 10 { break; }
}

I'd like to give to users of my crate the ability to iterate over my struct using for in construction, like this:

for next_game in game {
  println!("{:?}", next_game);
} 

Is it possible at all? How can I achieve this? How to make my code better and what I have to do with my struct?

Iterator implementation:

pub struct Game {
    /// The game hash
    pub hash: Vec<u8>
}

impl Iterator for Game {
    type Item = Game;

    fn next(&mut self) -> Option<Self::Item> {
        let mut hasher = Sha256::new();
        hasher.input(&hex::encode(&self.hash)); // we need to convert the hash into string first
        let result = hasher.result().to_vec();

        Some(Game {
            hash: result
        })
    }
}

Example: broken behavior with for

let mut game:Game = Game::new(&s).unwrap();
for g in game.take(2) {
    println!("{}", g);
}

Now if we run example, we will get two Game structs with same hash, while expected behavior is that the first g will have hash equal to SHA256(game.hash) and the next g's hash will be SHA256(SHA256(game.hash)). It works properly when I call .next().

like image 326
Vladimir Ignatyev Avatar asked Jan 26 '19 15:01

Vladimir Ignatyev


1 Answers

In the Rust iterators actually can be divided into 2 categories. Iterators which own the struct, thus can be created using .into_iter() which consumes self.

And iterators that iterate over structure without consuming it. They can be created be usually created using: .iter, .iter_mut()

For more information see related question: What is the difference between iter and into_iter? And documention: The three forms of iteration

To create iterator you should implement either IntoIterator trait, which will transform your structure into iterator or write functions which will create iterator: iter_mut, iter

pub fn iter_mut(&mut self) -> IterMut<T>

pub fn iter(&self) -> Iter<T>

So by convention you need 2 new types IterMut and Iter

impl Iterator for Iter {
    type Item = /* ... */;
    fn next(&mut self) -> Option<Self::Item> {
        /* ... */
    }
}

impl Iterator for IterMut {
    type Item = &mut /* ... */;
    fn next(&mut self) -> Option<Self::Item> {
        /* ... */
    }
}

They usually contain in them reference to the parent structure. For example for linked list it can be current node (which is updated every iteration). For array-like structures it can be index and reference to the parent, so index will incremented every time and element accessed using index operator and etc..

like image 166
Inline Avatar answered Nov 07 '22 18:11

Inline