Ben Chuanlong Du's Blog

It is never too late to learn.

Iterator in Rust

In [ ]:
:timing
:sccache 1

Tips & Traps

  1. Module std::collections has a good summary on when to each which collection in Rust.

  2. Be aware of possible performance penalty when you write functional programming style code especially when you have large collections.

  3. Functional programming methods (map, filter, etc.) can only apply to Itrators and Ranges instead of concrete collections types.

  4. itertools is a crate providing additional iterator related functionalities.

  5. Iterating a generic iterator (dyn Iterator<Item = T>) is slower than iterating a slice &[T] even though the generic iterator is backed by a slice.

  6. itertools::Itertools::combinations build an internal buffer first and then generate an iterator of combinations based on it. It can be faster to develop an iteration algorithm based a slice directly instead of an iterator. If performance is critical, it helps to pre-generate combinations and cache them.

filter

In [21]:
v1.iter().filter(|&&x| x > 0).count()
Out[21]:
9
In [23]:
v1.iter().max().unwrap()
Out[23]:
30
In [24]:
*v1.iter().max().unwrap()
Out[24]:
30
In [3]:
v1.iter().filter(|x| x > 0).count()
v1.iter().filter(|x| x > 0).count()
                         ^ expected `&&i32`, found integer
mismatched types

map

fold and reduce

Iterator.fold is preferred to Iterator.reduce if you do not want to return an Option.

scan

sum and product

take, take_while, skip, skip_while

In [2]:
let v = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
v
Out[2]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [4]:
v.iter().take(3)
Out[4]:
Take { iter: Iter([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), n: 3 }
In [7]:
v.iter().take(3).collect::<Vec<_>>()
Out[7]:
[0, 1, 2]
In [10]:
v.iter().take_while(|&&x| x < 3)
Out[10]:
TakeWhile { iter: Iter([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), flag: false }
In [11]:
v.iter().take_while(|&&x| x < 3).collect::<Vec<_>>()
Out[11]:
[0, 1, 2]
In [12]:
v.iter().skip(6)
Out[12]:
Skip { iter: Iter([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), n: 6 }
In [13]:
v.iter().skip(6).collect::<Vec<_>>()
Out[13]:
[6, 7, 8, 9]
In [15]:
v.iter().skip_while(|&&x| x < 6)
Out[15]:
SkipWhile { iter: Iter([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), flag: false }
In [16]:
v.iter().skip_while(|&&x| x < 6).collect::<Vec<_>>()
Out[16]:
[6, 7, 8, 9]

zip vs izip vs multizip

itertools::zip is equivalent to Iterator::zip in the standard library. itertools::multizip a generalization of itertools::zip and Iterator::zip to zip multip iterators. itertools::izip is a macro version of itertools::multizip and itertools::izip is preferred for its performance.

Join An Iterator of Strings

Empty Separator

In [5]:
let words = vec!["alpha", "beta", "gamma"];
let merged: String = words.into_iter().collect();
println!("{}", merged);
alphabetagamma

Notice that the above code won't work if an array were used.

In [4]:
let words = ["alpha", "beta", "gamma"];
let merged: String = words.into_iter().collect();
println!("{}", merged);
let merged: String = words.into_iter().collect();
                                       ^^^^^^^ value of type `String` cannot be built from `std::iter::Iterator<Item=&&str>`
a value of type `String` cannot be built from an iterator over elements of type `&&str`
help: the trait `FromIterator<&&str>` is not implemented for `String`

You have to dereference elements using the method .copied() to fix the issue.

In [2]:
let words = ["alpha", "beta", "gamma"];
let merged: String = words.into_iter().copied().collect();
println!("{}", merged);
alphabetagamma

The itertools Crate

The Rust crate itertools provides additional functionalities in addtion to those provided by the standard library.

In [2]:
:dep itertools = "0.10.0"
In [3]:
use itertools::Itertools;

intersperse

We had an example of concatenating an iterable of strings using the method std::iterator::collect. However, std::iterator::collect (into string) concatenate strings without seprators. itertools::itersperse lets you concatenate an iterable of string with a separator.

In [7]:
let words = ["alpha", "beta", "gamma"];
let merged: String = words.into_iter().intersperse(", ").collect();
println!("{}", merged);
let merged: String = words.into_iter().intersperse(", ").collect();
                                       ^^^^^^^^^^^ 
an associated function with this name may be added to the standard library in the future
help: call with fully qualified syntax `itertools::Itertools::intersperse(...)` to keep using the current method
let merged: String = words.into_iter().intersperse(", ").collect();
                                                   ^^^^ expected `&str`, found `str`
mismatched types
let merged: String = words.into_iter().intersperse(", ").collect();
                                                         ^^^^^^^ value of type `String` cannot be built from `std::iter::Iterator<Item=&&str>`
a value of type `String` cannot be built from an iterator over elements of type `&&str`
help: the trait `FromIterator<&&str>` is not implemented for `String`
In [5]:
let words = ["alpha", "beta", "gamma"];
let merged: String = words.iter().copied().intersperse(", ").collect();
println!("{}", merged);
alpha, beta, gamma
In [9]:
let data = vec![1, 1, 2, -2, 6, 0, 3, 1];
for chunk in &data.into_iter().chunks(3) {
    assert_eq!(4, chunk.sum());
}
Out[9]:
()
In [5]:
fn find_min<I>(vals: I) -> Option<&'static str> where I: Iterator<Item = &'static str>{
    vals.min_by_key(|v| v.len())
}
In [6]:
let s = ["how", "are", "you"];
In [10]:
find_min(s.iter())
find_min(s.iter())
^^^^^^^^ expected `str`, found `&str`
type mismatch resolving `<std::slice::Iter<'_, &str> as Iterator>::Item == &'static str`
In [20]:
fn foo<'a, I>(x: I) -> Option<I::Item> where I: IntoIterator<Item = &'a &'static str> {
    x.into_iter().min_by_key(|v| v.len())
}

let s = ["how", "are", "you"];
foo(&s)
Out[20]:
Some("how")
In [15]:
fn foo<'a, I: IntoIterator<Item=&'a i32>>(x: I) {
}

let v = vec![1, 2, 3];
foo(&v);
In [18]:
fn foo<'a, I>(x: I) -> i32 where I: IntoIterator<Item = &'a i32> {
    *x.into_iter().max().unwrap()
}

let v = vec![1, 2, 3];
foo(&v)
Out[18]:
3

group_by

The function group_by takes a function generating keys which are used to group elements in the iterator. However, notice that groups of elements are NOT just decided by the key function , it also depends on whether elements are consecutive . In short, consecutive elements that map to the same key ("runs") are assigned to the same group.

The IntoInterator Trait

Please refer to IntoIterator for discussions.

In [ ]:

Comments