Open9
Rust イテレータ分からん
構造体に対して、イテレータを返却するようなメソッドを作りたくなることは多い。
単純な例で考えていくぞ。
struct Items {
data: [i32; 3],
}
この Items
構造体に、順に i32
の値を返却するようなイテレータを返却するメソッドを作りたい
fn main() {
let items = Items {
data: [3, 7, 11],
};
for item in items.iter() {
println!("{item}");
}
}
期待する結果
3
7
11
まずは Items
に iter
メソッドを生やす
impl Items {
fn iter(&self) -> impl Iterator<Item = i32> {
todo!()
}
}
戻り値は i32
を順に返却してくるイテレータ。
(ちなみに todo! と書いてもコンパイラに怒られるけど、話に関係ないので無視)
iter
メソッドで返却される「順に i32
を返却するイテレータ」となる構造体を作る
struct ItemsIter {
current: usize,
target: &Items,
}
current
イテレートしている要素のインデックス。
target
として、要素を順に取り出す対象となる Items
への参照を保持する。
...これではコンパイルできない
error[E0106]: missing lifetime specifier
--> it/src/main.rs:24:13
|
24 | target: &Items,
| ^ expected named lifetime parameter
|
help: consider introducing a named lifetime parameter
|
22 ~ struct ItemsIt<'a> {
23 | current: usize,
24 ~ target: &'a Items,
|
コンパイラに言われるままに修正する。
struct ItemsIter<'a> {
current: usize,
target: &'a Items,
}
ItemsIter
をイテレータにするべく、Iterator
トレイトを実装する
impl<'a> Iterator for ItemsIter<'a> {
type Item = i32;
fn next(&mut self) -> Option<Self::Item> {
match self.current {
current @ (0 | 1 | 2) => {
self.current += 1;
Some(self.target[current])
}
_ => None
}
}
}
え?後置インクリメント演算子が欲しいだって!?
無いよ。
Items
の iter
メソッドでこの ItemsIter
を返却すれば完成!
impl Items {
fn iter(&self) -> impl Iterator<Item = i32> {
ItemsIter { current: 0, target: self }
}
}
...ではない。
error[E0700]: hidden type for `impl Iterator<Item = i32>` captures lifetime that does not appear in bounds
--> it/src/main.rs:15:9
|
14 | fn it(&self) -> impl Iterator<Item = i32> {
| ----- ------------------------- opaque type defined here
| |
| hidden type `ItemsIter<'_>` captures the anonymous lifetime defined here
15 | / ItemsIter {
16 | | current: 0,
17 | | target: self,
18 | | }
| |_________^
|
help: to declare that `impl Iterator<Item = i32>` captures `'_`, you can add an explicit `'_` lifetime bound
|
14 | fn it(&self) -> impl Iterator<Item = i32> + '_ {
| ++++
できた?わからん。
impl Items {
fn iter(&self) -> impl Iterator<Item = i32> + '_ {
ItemsIter { current: 0, target: self }
}
}
エラーメッセージの語彙が難しい
- hidden type
- anonymous lifetime
- opaque type
iter
メソッドを impl Trait
を使わずに、明示的に型を指定すると次のようになる。
impl Items {
fn iter<'a>(&'a self) -> ItemsIter<'a> {
ItemsIter<'a> { current: 0, target: self }
}
}
ItemsIter
の型制約は
impl<'a> Iterator for ItemsIter<'a> { ... }
なので、impl Iterator<Items =...>
ではなくて impl Iterator<Items = ...> + 'a
ということ(多分)
ライフタイムパラメータを 'a
でなく、無名で'_
と書ける条件はよくわからん。
impl Items {
fn iter(&self) -> ItemsIter<'_> {
ItemsIter { current: 0, target: self }
}
}
impl Items {
fn iter(&self) -> Impl Iterator<Item=i32> + '_ {
ItemsIter { current: 0, target: self }
}
}