📖

[Rust] Iterator トレイトで自分がよく使うメソッドまとめ

に公開

一覧

メソッド名 役割
all 全ての要素が条件を満たすかの確認
any 条件を満たす要素が1つでもあるかの確認
cloned イテレータの各要素をクローンして、新しいイテレータを作成する。
collect イテレータをコレクションに変換する
count 要素数(ただし、イテレータを消費する)
filter 条件を満たす要素のみで、新たにイテレータを作成する
filter_map filterとmapの機能を組み合わせたもの
find 条件を満たす最初の要素を返す
find_map findとmapの機能を組み合わせたもの
map 各要素にクロージャ適用して、新たにイテレータを作成する
position 条件を満たす最初の要素のインデックスを返す
unzip タプルのイテレータを2つの別々のコレクションに分割する
zip 2つのイテレータの各要素ペア(タプル)として、1つの新たなイテレータを作成する

🔸 all

各要素に対して、bool型を返すクロージャーを適用して、全ての要素で、trueになる場合は、trueを返す。
1つでもfalseになる要素がある場合は、falseを返す。

定義

fn all<F>(&mut self, f: F) -> bool
where
    Self: Sized,
    F: FnMut(Self::Item) -> bool,

let vec = vec![1, 2, 3, 4, 5];

println!("{}", vec.iter().all(|x| *x < 5));  // false
println!("{}", vec.iter().all(|x| *x < 10)); // true
 

🔸 any

各要素に対して、bool型を返すクロージャーを適用して、1つでもtrueになる要素がある場合は、trueを返す。
全ての要素でfalseになる場合は、falseを返す。

定義

fn any<F>(&mut self, f: F) -> bool
where
    Self: Sized,
    F: FnMut(Self::Item) -> bool,

let vec = vec![1, 2, 3, 4, 5];

println!("{}", vec.iter().any(|x| *x > 6));  // false
println!("{}", vec.iter().any(|x| *x >= 5)); // true

🔸 cloned

イテレータの各要素対してクローンを行なって、新しいイテレータを作成する。
また、&T(参照)のイテレータをT(値)のイテレータに変換する。
filterなど、要素数が変化するものに対しては、cloneの実行数を減らすために、可能な限り処理の最後にcloned()を配置するとパフォーマスが良くなる。

定義

fn cloned<'a, T>(self) -> Cloned<Self> 
where
    T: 'a + Clone,
    Self: Sized + Iterator<Item = &'a T>,

let vec = vec![1,2,3,4,5];
let result: Vec<&i32> = vec.iter().collect();
println!("{:?}", result); // [1, 2, 3, 4, 5]

let result_cloned: Vec<i32> = vec.iter().cloned().collect();
println!("{:?}", result_cloned); // [1, 2, 3, 4, 5]

🔸 collect

イテレータをコレクションに変換する。
FromIteratorトレイトを実装している任意の型に変換できるため、以下のいずれかの方法で型を指定する必要がある。

    1. turbofish構文(::<>)を使用
    1. 変数の型注釈を使用

また、イテレータを消費する。

定義

fn collect<B>(self) -> B
where
    B: FromIterator<Self::Item>,
    Self: Sized,

let vec = vec![1, 2, 3, 4, 5];
let result_turbo_fish = vec.iter().map(|x| x + 1).collect::<Vec<i32>>();
let result_inference: Vec<i32> = vec.iter().map(|x| x + 1).collect();

println!("{:?}", result_turbo_fish);
println!("{:?}", result_inference);

🔸 count

Noneが見つかるまで、next関数を呼び出し続けて、見つかったSome数を返す(イテレータの要素数を返す)
ただし、イテレーターを消費する(consume)ため、呼び出し後は、イテレータを使用できなくなる。

定義

fn count(self) -> usize
where
    Self: Sized,

let vec = vec![1, 2, 3, 4, 5];
println!("{}", vec.iter().count()); // 5

🔸 filter

各要素に対して、bool型を返すクロージャーを適用して、trueとなる要素から新たにイテレータを作成する。
クロージャーの引数は「参照」を受け取るため、参照の分配で「&」を使用して、参照を外すのが良い。
ただし、遅延評価のため、実際にイタレータが消費されるまで、計算が行われない。

定義

fn filter<P>(self, predicate: P) -> Filter<Self, P> 
where
    Self: Sized,
    P: FnMut(&Self::Item) -> bool,

let vec = vec![1, 2, 3, 4, 5];
let result_iter = vec.iter().filter(|&x| x % 2 == 0);
println!(" {:?}", result_iter); //   Filter { iter: Iter([1, 2, 3, 4, 5]) }

// collectにより、vecに変換して、イタレータが消費されて、filterのクロージャーの処理が行われる。
let result_vec = result_iter.collect::<Vec<&i32>>();
println!(" {:?}", result_vec); // [2, 4]

🔸 filter_map

各要素に、Optionを返すクロージャーを適用し、Someを返す要素のみで、新たにイテレータを作成する。
filtermapの機能を組み合わせたもの。

定義

fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F> 
where
    Self: Sized,
    F: FnMut(Self::Item) -> Option<B>,

filter_map
// 偶数のみを取り出して3倍する
let vec = vec![1, 2, 3, 4, 5];
let result = vec.iter().filter_map(|&x| {
    if x % 2 == 0 {
        Some(x * 3)
    } else {
        None
    }
});
println!("{:?}", result.collect::<Vec<i32>>()); // [6, 12]
filter + map
let vec = vec![1, 2, 3, 4, 5];
let result = vec.iter().filter(|&x| x % 2 == 0).map(|x| x * 3);
println!("{:?}", result.collect::<Vec<i32>>()); // [6, 12]

🔸 find

各要素に対して、bool型を返すクロージャーを適用して、trueとなる最初の要素をSome(elment)として返す。
全ての要素でfalseになる場合は、Noneを返す。
filterと同じく、クロージャーの引数は「参照」を受け取るため、参照の分配で「&」を使用して、参照を外すのが良い。

定義

fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
where
    Self: Sized,
    P: FnMut(&Self::Item) -> bool,

let vec = vec![1, 2, 3, 4, 5];
let result_some = vec.iter().find(|&x| x * 3 > 10);
println!(" {:?}", result_some.unwrap()); // 4


let result_none = vec.iter().find(|&x| x % 10 == 0);
println!(" {:?}", result_none); // None

🔸 find_map

各要素に、Optionを返すクロージャーを適用し、最初にSomeを返す要素を返す。
findmapの機能を組み合わせたもの。

定義

fn find_map<B, F>(&mut self, f: F) -> Option<B>
where
    Self: Sized,
    F: FnMut(Self::Item) -> Option<B>,

find_map
let vec = vec![1, 2, 3, 4, 5];
let result_some = vec.iter().find_map(|&x| if x % 2 == 0 { Some(x * 3) } else { None });
println!(" {:?}", result_some.unwrap()); // 6


let result_none = vec.iter().find_map(|&x| if x % 10 == 0 { Some(x * 3) } else { None });
println!(" {:?}", result_none); // None
find + map
let vec = vec![1, 2, 3, 4, 5];
let result_find_map_some = vec.iter().find(|&x| x % 2 == 0).map(|&x| x * 3);
println!(" {:?}", result_find_map_some.unwrap()); // 6

let result_find_map_none = vec.iter().find(|&x| x % 10 == 0).map(|&x| x * 3);
println!(" {:?}", result_find_map_none); // None

🔸 map

各要素にクロージャーを適用し、クロージャーの返り値から、新たにイテレータを作成する。
ただし、遅延評価のため、実際にイタレータが消費されるまで、計算が行われない。

let vec = vec![1, 2, 3, 4, 5];
// 中身を表示しているだけのため、mapのクロージャーの処理はまだ行われていない
let result_iter= vec.iter().map(|x| x * 2);
println!(" {:?}", result_iter); //  Map { iter: Iter([1, 2, 3, 4, 5]) }

// collectにより、vecに変換して、イタレータが消費されて、mapのクロージャーの処理が行われる。
let result_vec = result_iter.collect::<Vec<i32>>();
println!(" {:?}", result_vec); // [2, 4, 6, 8, 10]

🔸 position

各要素に対して、bool型を返すクロージャーを適用して、trueとなる最初の要素のインデックスをSome(i)として返す。
trueとなる要素が1つもない場合は、Noneを返す。

定義

fn position<P>(&mut self, predicate: P) -> Option<usize>
where
    Self: Sized,
    P: FnMut(Self::Item) -> bool,

let vec = vec![1, 2, 3, 4, 5];
let result_some = vec.iter().position(|&x| x == 3);
println!(" {:?}", result_some.unwrap()); // 2


let result_none = vec.iter().position(|&x| x % 10 == 0);
println!(" {:?}", result_none); // None

🔸 unzip

タプルのイテレータを2つの別々のコレクションに分割する。
左側の要素と右側の要素のそれぞれでコレクションが作成される。

定義

fn unzip<A, B, FromA, FromB>(self) -> (FromA, FromB)
where
    FromA: Default + Extend<A>,
    FromB: Default + Extend<B>,
    Self: Sized + Iterator<Item = (A, B)>,

let vec = vec![(1, "a"), (2, "b"), (3, "c"), (4, "d"), (5, "e")];
let (result_left, result_right): (Vec<i32>, Vec<&str>) = vec.iter().cloned().unzip();

println!("{:?}", result_left); // [1, 2, 3, 4, 5]
println!("{:?}", result_right); // ["a", "b", "c", "d", "e"]

🔸 zip

2つのイテレータの要素を組み合わせて、新しいイテレータを作成する。
各要素は(a, b)の形式のタプルとして返される。
短い方のイテレータの長さに合わせて処理が終了する。
(0..)で無限イテレータを作成し、zipと組み合わせることで、インデックス付きのイテレーションを作成できる。

定義

fn zip<U>(self, other: U) -> Zip<Self, <U as IntoIterator>::IntoIter>
where
    Self: Sized,
    U: IntoIterator,

let vec = vec![1, 2, 3, 4, 5];
let vec2 = vec![6, 7, 8, 9];

let result_zip = vec.iter().zip(vec2.iter());

// a: 1, b:6
// a: 2, b:7
// a: 3, b:8
// a: 4, b:9
for (a, b) in result_zip {
    println!("a: {}, b:{}", a, b); 
}

let result_index = (0..).zip(vec2.iter());

// a: 0, b:6
// a: 1, b:7
// a: 2, b:8
// a: 3, b:9
// a: 4, b:10
for (a, b) in result_index {
    println!("a: {}, b:{}", a, b); 
}

typescriptとの比較表

自分がよくTypeScriptを使用するため、その比較表

Rust TypeScript
all every
any some
cloned slice/map/[...array](スプレッド構文)など
collect Array.from
count length
filter filter
filter_map filter+map
find find
find_map find+map
map map
position findIndex
unzip -
zip -

参考

https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.all

Discussion