1、什么是迭代器
迭代器模式:对一系列项执行某些任务
迭代器负责:
遍历每个项
确定序列(遍历)何时完成
Rust的迭代器:
懒惰的:除非调用消费迭代器的方法,否则迭代器本身没有任何效果。
fn main() {
let v1 = vec![1,2,3];
let v1_iter = v1.iter();
for val in v1_iter {
println!("Got {}",val)
}
}
2、lterator trait
所有迭代器都实现了lterator trait
lterator trait定义于标准库,定义大致如下:
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::ltem>;
// methods with default implementations elided
}
type ltem和 Self::ltem定义了与此该trait 关联的类型。
实现lterator trait需要你定义一个ltem类型,它用于 next方法的返回类型(迭代器的返回类型)。
lterator trait仅要求实现一个方法: next
next:
每次返回迭代器中的一项
返回结果包裹在Some里
迭代结束,返回None
可直接在迭代器上调用next方法
#[cfg(test)]
mod tests {
#[test]
fn iterator_demonstration() {
let v1 = vec![1,2,3];
let mut v1_iter = v1.iter();
assert_eq!(v1_iter.next(),Some(&1));
assert_eq!(v1_iter.next(),Some(&2));
assert_eq!(v1_iter.next(),Some(&3));
}
}
几个迭代方法
iter方法:在不可变引用上创建迭代器
into_iter方法:创建的迭代器会获得所有权
iter_mut方法:迭代可变的引用
3、消耗/产生迭代器
消耗迭代器的方法
在标准库中,lterator trait有一些带默认实现的方法
其中有一些方法会调用next方法
实现lterator trait时必须实现next 方法的原因之一
调用next的方法叫做“消耗型适配器”
因为调用它们会把迭代器消耗尽
sum方法(就会耗尽迭代器)
取得迭代器的所有权
通过反复调用next,遍历所有元素
每次迭代,把当前元素添加到一个总和里,迭代结束,返回总和
#[cfg(test)]
mod tests {
#[test]
fn iterator_sum() {
let v1 = vec![1,2,3];
let v1_iter = v1.iter();
let total:i32 = v1_iter.sum();
assert_eq!(total,6);
}
}
产生其它迭代器的方法
定义在 lterator trait上的另外一些方法叫做“迭代器适配器”
―把迭代器转换为不同种类的迭代器
可以通过链式调用使用多个迭代器适配器来执行复杂的操作,这种调用可读性较高。
例如: map
一接收一个闭包,闭包作用于每个元素
-产生一个新的迭代器
#[cfg(test)]
mod tests {
#[test]
fn iterator_sum() {
let v1 = vec![1,2,3];
let v2:Vec<_> = v1.iter().map(|x|x+1).collect();
assert_eq!(v2,vec![2,3,4])
}
}
这个Vec<_>就表示让编译器自己推断集合的值
collect 方法:消耗型适配器,把结果收集到一个集合类型中。
4、使用闭包捕获环境
filter方法:
一接收一个闭包
一这个闭包在遍历迭代器的每个元素时,返回bool类型
一如果闭包返回true:当前元素将会包含在filter产生的迭代器中
一如果闭包返回false:当前元素将不会包含在 filter 产生的迭代器中
#[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(|x|x.size == shoe_size).collect()
}
#[test]
fn filiter_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("bool"),
},
];
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("bool"),
},
]
);
}
5、创建自定义迭代器
使用lterator trait来创建自定义迭代器
实现next方法
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> {
if self.count < 5 {
self.count += 1;
Some(self.count)
} else {
None
}
}
}
#[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);
}
#[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这个函数是把两个数组的数据合并到一个迭代器里面
6、改进I/○项目(minigrep)
// TODO 待续
lib.rs
use std::fs;
use std::error::Error;
use std::env;
pub fn run(config:Config) -> Result<(),Box<dyn Error>> {
let contents = fs::read_to_string(config.filename)?;
let results = if config.case_sensitive {
search(&config.query, &contents)
} else {
search_case_insensitive(&config.query, &contents)
};
for line in results {
println!("{}",line)
}
Ok(())
}
pub struct Config {
pub query:String,
pub filename:String,
pub case_sensitive:bool
}
impl Config {
pub fn new(mut args: std::env::Args) -> Result<Config,&'static str> {
if args.len() < 3 {
return Err("没有足够的参数");
}
args.next();
let query = match args.next() {
Some(s) => s,
None=>return Err("获取不到查询的参数")
};
let filename = match args.next() {
Some(s) => s,
None=>return Err("获取不到文本路径参数")
};
let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
Ok(Config{query,filename,case_sensitive})
}
}
pub fn search<'a>(query:&str,contents:&'a str) -> Vec<&'a str> {
// let mut results = Vec::new();
// for line in contents.lines() {
// if line.contains(query) {
// results.push(line.trim());
// }
// }
// results
contents.lines().filter(|content|content.contains(query)).collect()
}
pub fn search_case_insensitive<'a>(query:&str,contents:&'a str) -> Vec<&'a str> {
// let mut results = Vec::new();
// let query = query.to_lowercase();
// for line in contents.lines() {
// if line.to_lowercase().contains(&query) {
// results.push(line.trim());
// }
// }
// results
let query = query.to_lowercase();
contents.lines().filter(|content|content.to_lowercase().contains(&query)).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn case_sensitive() {
// 区分大小写
let query = "duct";
let contents = "\
Rust
safe,fast,productive.
Pick three.
Duck tape.
";
assert_eq!(vec!["safe,fast,productive."],search(query,contents))
}
#[test]
fn case_insensitive() {
// 不区分大小写
let query = "rUSt";
let contents = "\
Rust:
safe,fast,productive.
Pick three.
Trust me.
";
assert_eq!(vec!["Rust:","Trust me."],search_case_insensitive(query,contents))
}
}
main.rs
use std::env;
use minigrep::Config;
use std::process;
fn main() {
let config = Config::new(env::args()).unwrap_or_else(|err|{
eprintln!("参数解析失败:{}",err);
process::exit(1);
});
if let Err(e) = minigrep::run(config) {
eprintln!("程序错误{}",e);
process::exit(1);
}
}
7、循环vs 迭代器
迭代器是rust里面的高级抽象,但是迭代器在编译后生成的代码几乎我们手写的代码时一样的产物,这在rust里面被称为:
零开销抽象
Zero-Cost Abstraction。使用抽象时不会引入额外的运行时开销。