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等