1 闭包
1.1 闭包与局部函数
fn main(){
// 不能这样定义匿名函数
// let t = fn(t:i32,x:&i32) -> i32 { t + *x };
// 这样(嵌套地)定义局部函数是合法的
fn t(t:i32,x: &i32) -> i32 { t + *x };
assert_eq!(vec![1,2,3].iter().fold(0,t),6);
// 但通常使用更简洁的闭包: 通常不指定闭包参数和返回值的类型,而使用自动类型推导
// 而对于函数,不能省略参数和返回值类型
assert_eq!(vec![1,2,3].iter().fold(0,|a,x| a + x),6);
}
1.2 闭包可以捕获环境中的变量
fn main() {
let x = 4;
// 闭包可以使用环境中的变量 x
let equal_to_x = |z| z == x;
// 不合法: 函数不能使用环境中的变量 x,函数中 x 没有定义
//fn equal_to_x2(z : i32) -> bool { z == x }
let y = 4;
assert!(equal_to_x(y));
}
1.3 闭包捕获环境的方式
- 闭包有三种可能的环境捕获方式:
Fn
:以不可变引用的方式使用环境中的变量FnMut
:以可变引用的方式使用环境中的变量FnOnce
:将获取环境中变量的所有权,无法在闭包之外使用被捕获的变量,也无法多次调用闭包
- 编译器自动分析代码,采用合适的方式进行环境捕获
- 可以在闭包前用
move
明确指示,将使用到的变量的所有权移动到闭包中,通常用在启动线程的时候
fn main() {
let t = String::from("abc");
//闭包捕获了环境中的变量t,因为String类型的加运算要求所有权,所以闭包的类型是FnOnce: t的所有权被移动到闭包中
let d = ||println!("{}",t + "def");
//println!("{}",t);// 这里无法使用所有权被移动的变量t
d();
//d();// FnOnce类型的闭包不能被两次调用:变量t只能被移动一次到闭包中
//println!("{}",t);// 这里无法使用所有权被移动的变量t
}
2 迭代器
2.1 迭代器特性Iterator
trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
fn main() {
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));
assert_eq!(v1_iter.next(), None);
}
// 注意: 大括号是必须的,如果没有前面的大括号,则仍然有效的v1_iter是对v1的借用
// 下面的 v1.into_iter() 就无法转移 v1 的所有权了
let mut v1_iter = v1.into_iter();
assert_eq!(v1_iter.next(), Some(1));// 元素是所有权类型
assert_eq!(v1_iter.next(), Some(2));
assert_eq!(v1_iter.next(), Some(3));
assert_eq!(v1_iter.next(), None);
}
2.2.1 消费适配器
Iterator
特性的各种调用next()
方法的方法称作消费适配器(consuming adaptors)- 常见的消费适配器有:
min
、max
、last
、sum
等
2.2.2 迭代器适配器
Iterator
特性的一些返回一个新迭代器的方法,称作迭代器适配器(iterator adaptors)- 常见的迭代器适配器有:
take
、skip
、rev
、cycle
等