まずは、空のtypeと付随するiteratorを作ってみる
struct A {}
struct AIter {
now: usize,
}
impl A {
fn new() -> A {
A {}
}
fn iter(&self) -> AIter {
AIter { now: 0 }
}
}
impl Iterator for AIter {
type Item = usize;
fn next(&mut self) -> Option<usize> {
self.now += 1;
Some(self.now - 1)
}
}
fn main() {
let a = A::new();
assert_eq!(
vec![0, 1, 2, 3, 4],
a.iter().take(5).collect::<Vec<usize>>()
);
}
playground
特につまるところはなし。iterの引数には&つけないと、iter()よぶとmoveされてしまう。
次にAにVecを足してその値をiteratorでとりだせるようにしてみる
struct A {
v: Vec<i32>,
}
struct AIter<'a> {
a: &'a A,
now: usize,
}
impl A {
fn new(v: Vec<i32>) -> A {
A { v: v }
}
fn iter(&self) -> AIter {
AIter { a: &self, now: 0 }
}
}
impl<'a> Iterator for AIter<'a> {
type Item = i32;
fn next(&mut self) -> Option<i32> {
self.now += 1;
if self.now - 1 < self.a.v.len() {
Some(self.a.v[self.now - 1])
} else {
None
}
}
}
fn main() {
let a = A::new(vec![6, 7, 8, 9, 10, 11, 12]);
assert_eq!(vec![6, 7, 8, 9, 10], a.iter().take(5).collect::<Vec<i32>>());
}
playground
Iteratorの中にreferenceをもたせてその値を使用する。
もう少しややこしくして、Vec<String>
にしつつ、&str
をiteratorでとりだせるようにする。
struct A {
v: Vec<String>,
}
struct AIter<'a> {
a: &'a A,
now: usize,
}
impl A {
fn new(v: Vec<&str>) -> A {
A {
v: v.iter().map(|s| s.to_string()).collect(),
}
}
fn iter(&self) -> AIter {
AIter { a: &self, now: 0 }
}
}
impl<'a> Iterator for AIter<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<&'a str> {
self.now += 1;
if self.now - 1 < self.a.v.len() {
Some(&self.a.v[self.now - 1])
} else {
None
}
}
}
fn main() {
let a = A::new(vec!["a", "b", "c", "d", "e", "f", "g"]);
assert_eq!(
vec!["a", "b", "c", "d", "e"],
a.iter().take(5).collect::<Vec<&str>>()
);
}
playground
Vec<i32>
をVec<String>
にしてOption<i32>
をOption<&str>
にする
Stringと&strのあたりはあまり自信なし
ここからが本題。2種類のIteratorをつくってtake(5)を関数にしてみる
struct A {
v: Vec<String>,
}
struct AIter<'a> {
a: &'a A,
now: usize,
}
struct AIter2 {
now: usize,
}
impl A {
fn new(v: Vec<&str>) -> A {
A {
v: v.iter().map(|s| s.to_string()).collect(),
}
}
fn iter(&self) -> AIter {
AIter { a: &self, now: 0 }
}
fn iter2(&self) -> AIter2 {
AIter2 { now: 0 }
}
}
impl<'a> Iterator for AIter<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<&'a str> {
self.now += 1;
if self.now - 1 < self.a.v.len() {
Some(&self.a.v[self.now - 1])
} else {
None
}
}
}
impl Iterator for AIter2 {
type Item = usize;
fn next(&mut self) -> Option<usize> {
self.now += 1;
Some(self.now - 1)
}
}
fn take5<T, I : Iterator<Item = T>>(iter: I) -> Vec<T> {
iter.take(5).collect()
}
//fn take5<T>(iter: impl Iterator<Item = T>) -> Vec<T> {
// iter.take(5).collect()
//}
fn main() {
let a = A::new(vec!["a", "b", "c", "d", "e", "f", "g"]);
assert_eq!(vec!["a", "b", "c", "d", "e"], take5(a.iter()));
assert_eq!(vec![0, 1, 2, 3, 4], take5(a.iter2()));
}
playground
genericsとimpl Traitをつかう。impl Traitがない時代はどうしてたのかは不明。こういう場合には、IntoIteratorは使えない。
impl Traitを使う必要はない。
assert_eq!はやめて、take5の中でforでprintln!するようにしてみる。以下は抜粋
fn take5<T: std::fmt::Display>(iter: impl Iterator<Item = T>) {
for i in iter.take(5) {
println!("{}", i);
}
}
fn main() {
let a = A::new(vec!["a", "b", "c", "d", "e", "f", "g"]);
take5(a.iter());
take5(a.iter2());
}