要素を順番に返すヤツ。map
, zip
, filter
, skip
などのコレクション操作もできる。
成果物
参考
イテレータ
イテレータは要素を順に取り出す。Option<Some<T>,None>
型を返す。これにより終了判断できる。
生成(Iterator.iter()
)
Vec型のイテレータはiter()
関数の戻り値で得られる。
fn main() { let v = vec![1,2,3]; let i = v.iter(); println!("{:?}", i); // Iter([1, 2, 3]) }
メソッド | 概要 |
---|---|
Iterator.iter() |
不変参照 |
Iterator.iter_mut() |
可変参照 |
Iterator.into_iter() |
所有権を奪う |
for
fn main() { let v = vec![1,2,3]; let i = v.iter(); for value in v { println!("{}", value); } // 1\n2\n3 }
Iterator.next()
fn main() { let v = vec![1,2,3]; let mut i = v.iter(); // next()を呼ぶならmutableにする必要がある。さもなくばerror[E0596]: cannot borrow `i` as mutable, as it is not declared as mutable assert_eq!(i.next(), Some(&1)); assert_eq!(i.next(), Some(&2)); assert_eq!(i.next(), Some(&3)); assert_eq!(i.next(), None); }
Iterator
トレイト
Iteratorトレイトは標準ライブラリで以下のように定義されている。全てのイテレータはこれを実装している。
pub trait Iterator { type Item; fn next(&mut self) -> Option<Self::Item>; // ... }
消費アダプタ(consuming adaptors)
next()
を呼び出すメソッドは消費アダプタと呼ばれる。
sum()
はその一例。
fn main() { let v = vec![1,2,3]; let i = v.iter(); let s:i32 = i.sum(); assert_eq!(s, 6); // println!("{:?}", i); // error[E0382]: borrow of moved value: `i` println!("{:?}", v); }
なお、sum()
の戻り値には型注釈が必須。さもなくば以下エラー。
error[E0282]: type annotations needed --> src/main.rs:4:9 | 4 | let s = i.sum(); | ^ | | | cannot infer type | consider giving `s` a type
また、sum()
は所有権を奪う。よってイテレータ変数i
はsum()
の呼出後、使えない。
let s:i32 = i.sum(); println!("{:?}", i); // error[E0382]: borrow of moved value: `i`
イテレータアダプタ(iterator adaptors)
src/main.rs
fn main() { let v = vec![1,2,3]; v.iter().map(|x| x + 1); }
map()
はまだ何もしていない。イテレータを消費するまで実行しない。collect()
で消費する。
$ cargo run ... warning: unused `std::iter::Map` that must be used --> src/main.rs:3:5 | 3 | v.iter().map(|x| x + 1); | ^^^^^^^^^^^^^^^^^^^^^^^^ | = note: #[warn(unused_must_use)] on by default = note: iterators are lazy and do nothing unless consumed```
map()
src/main.rs
fn main() { let v = vec![1,2,3]; let v2:Vec<_> = v.iter().map(|x| x + 1).collect(); assert_eq!(v2, vec![2, 3, 4]); }
map()
は主語となるイテレータの各要素すべてに対して、引数で渡したクロージャの戻り値を要素としたVecを返す。
filter()
クロージャがtrue
を返せば、イテレータにその要素が含まれる。false
を返せば含まれない。
src/lib.rs
#[derive(PartialEq, Debug)] struct Shoe { size: u32, style: String, } fn shoes_in_my_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> { shoes.into_iter() .filter(|s| s.size == shoe_size) .collect() } #[cfg(test)] mod test { use super::*; #[test] fn filters_by_size() { let shoes = vec![ Shoe { size: 10, style: String::from("sneaker") }, Shoe { size: 13, style: String::from("sandal") }, Shoe { size: 10, style: String::from("boot") }, ]; let in_my_size = shoes_in_my_size(shoes, 10); assert_eq!( in_my_size, vec![ Shoe { size: 10, style: String::from("sneaker") }, Shoe { size: 10, style: String::from("boot") }, ] ); } }
$ cargo test
独自イテレータ実装
src/lib.rs
struct Counter { count: u32, } impl Counter { fn new() -> Counter { Counter { count: 0 } } } impl Iterator for Counter { type Item = u32; fn next(&mut self) -> Option<Self::Item> { self.count += 1; if self.count < 6 { Some(self.count) } else { None } } } #[cfg(test)] mod test { use super::*; #[test] fn calling_next_directly() { let mut counter = Counter::new(); assert_eq!(counter.next(), Some(1)); assert_eq!(counter.next(), Some(2)); assert_eq!(counter.next(), Some(3)); assert_eq!(counter.next(), Some(4)); assert_eq!(counter.next(), Some(5)); assert_eq!(counter.next(), None); } }
zip
, skip
#[test] fn using_other_iterator_trait_methods() { let sum: u32 = Counter::new().zip(Counter::new().skip(1)) .map(|(a, b)| a * b) .filter(|x| x % 3 == 0) .sum(); assert_eq!(18, sum); }
各メソッドは上記の意味である。よってzip
は以下の2値がタプルとなったイテレータを返す。
a | b |
---|---|
1 | 2 |
2 | 3 |
3 | 4 |
4 | 5 |
5 | None |
None | None |
ただし、zip
は少なくとも片方がNone
であればそのタプルを生成しない。つまりzip
が返すのは以下4組のみ。
a | b |
---|---|
1 | 2 |
2 | 3 |
3 | 4 |
4 | 5 |
zip
が返すイテレータをnext()
すると順次以下を返す。
返却値 |
---|
(1,2) |
(2,3) |
(3,4) |
(4,5) |
None |
タプルの各値をかけ合わせ、3で割り切れるか否か。
a | b | a*b | x%3==0 |
---|---|---|---|
1 | 2 | 1*2=2 | 2%3=2 false |
2 | 3 | 2*3=6 | 6%3=1 true |
3 | 4 | 3*4=12 | 12%3=0 true |
4 | 5 | 4*5=20 | 20%3=2 false |
3で割り切れるものは6
, 12
の2つ。これらをsum
すると、18
となる。assert_eq!(18, sum);
で確認。
対象環境
- Raspbierry pi 3 Model B+
- Raspbian stretch 9.0 2018-11-13
- bash 4.4.12(1)-release
- rustc 1.34.2 (6c2484dc3 2019-05-13)
- cargo 1.34.0 (6789d8a0a 2019-04-01)
$ uname -a Linux raspberrypi 4.19.42-v7+ #1219 SMP Tue May 14 21:20:58 BST 2019 armv7l GNU/Linux
前回まで
- Rustを学んでみたい(プログラミング言語)
- Rustの環境構築
- RustでHelloWorld
- Rustの和訳ドキュメント
- Cargoでプロジェクト作成・ビルド・実行
- クレートとは?
- Rustで関数を使ってみる
- Rustでモジュールを使ってみる
- Rustで乱数を生成する(rand)
- Rustで標準入力する(std::io::stdin().read_line())
- RustでMatch判定する(match)
- Rustでprintとread_lineを1行にする方法
- Rustで数当てゲーム
- クレート名にドット.が使えない
- Rustの変数と可変性(let, mut) error[E0384]: cannot assign twice to immutable variable
x
- Rustのimmutable束縛とconst定数の違い
- RustのREPL、evcxrのインストールに失敗した
- Rustでコンパイルするときの変数未使用warningを消す
- Rustの変数(再代入、再宣言(シャドーイング))
- Rustのシャドーイングについて
- イミュータブルについて(副作用)
- Rustの定数(const)
- Rustのデータ型(数値)
- Rustのデータ型(論理)
- Rustのデータ型(文字)
- Rustのデータ型(タプル)
- Rustのデータ型(配列)
- Rustの関数
- Rustのif式
- Rustのくりかえし文(loop)
- Rustのくりかえし文(while)
- Rustのくりかえし文(for)
- Rustの所有権(ムーブ)
- Rustの所有権(関数)
- Rustの所有権(スライス)
- Rustの構造体(定義とインスタンス化)
- Rustの構造体(プログラム例)
- Rustの構造体(メソッド)
- Rustの列挙型(enum)
- Rustの列挙型(enum)
- Rustの列挙型(enum)
- Rustのmatch(制御フロー演算子)
- RustでNULLを扱う(Option, Some, None)
- NULL参照は10億ドルの失敗だった
- Rustの列挙型に独自表示を実装する(E0277 対策 std::fmt::Display 実装)
- RustのIfLet(matchの糖衣構文)
- Rustのプロジェクト構造
- Rustのcargoでライブラリ&テスト(単体、結合)
- Rustのモジュール(mod)
- Rustのモジュール(pub)
- Rustのmod参照方法(
mod 子モジュール名;
,use 要素名;
,extern crate クレート名;
,super
) - Rustのインポートまとめ(Rust2018)
- RustのコレクションVec型
- RustのコレクションString型
- RustのコレクションHashMap型
- Rustのコレクション(練習問題)
- Rustのエラー処理
- Rustのジェネリクス
- Rustのトレイト
- Rustのライフタイム1
- Rustのライフタイム2(構造体の定義)
- Rustのライフタイム3(ライフタイム省略)
- Rustのライフタイム4(impl定義)
- Rustの静的ライフタイム5('static)
- Rustのライフタイム6(ジェネリクス、トレイト境界とともに)
- Rustのテストコードを書く
- Rustのテスト実行
- Rustのテスト体系化
- Rustでコマンドライン引数を受け取る
- Rustのファイル読込
- Rustでリファクタリング(モジュール性とエラー処理の向上)
- Rustでテスト駆動開発
- Rustで環境変数を取得する
- RustでStdErr出力
- Rustのクロージャ