Rust基础学习笔记(四):闭包与迭代器
本章主要记录Rust中闭包与迭代器的用法,并用其改善现有代码以提升性能。所有代码来自官方文档。
闭包
闭包是保存在变量里、作为参数传递给其他函数的匿名函数。其特点是能够捕获其所在定义区间内的变量值。
定义
let value = |para1,para2|{ ... };
Value是闭包赋予的变量名,||中间是所需的参数(多个参数由逗号分隔)
使用
println!("Today, do {} pushups!", expensive_closure(intensity));
类型
闭包不必要声明其参数和返回值的类型,只有一行代码时甚至可以省略大括号:
let expensive_closure = |num: u32| -> u32 x+1;
而一旦被使用,闭包的参数与返回值的类型便会确定,之后若出现不匹配则会报错:
let example_closure = |x| x; let s = example_closure(String::from("hello")); let n = example_closure(5); //在这一行会报错!
通过泛型参数和Fn特性存储闭包
通过这个特性可以保留闭包的计算结果避免重复调用或冗余代码,保存在一个结构体中。
这个结构体使用一个实现了Fn、FnOnce或者FnMut的泛型,并需要指定闭包参数、返回值的结构:
struct Cather<T> where T: Fn(u32) -> u32, { calculation: T, value: Option<u32>, }
可以看出,如果闭包不传递参数的话,也许最好运用函数来替代。
然后要设置一个保存其值的机制:
impl<T> Cacher<T> where T: Fn(u32) -> u32, { fn new(calculation: T) -> Cacher<T> { Cacher { calculation, value: None, } } fn value(&mut self, arg: u32) -> u32{ match self.value { Some(v) => v, None => { let v = (self.calculation)(arg); self.value = Some(v); v } } } }
然后在新建闭包时用Cacher::new方法,使用闭包时用Cacher::value方法。
这种方法存在的限制
显然这种方法默认每次调用闭包时参数都是一致的。可以改造结构体,使其加载一个哈希表来解决这一问题。
这种方法还只允许使用一个泛型,不过可以手动增加。
捕获定义域中的变量
闭包可以捕获其所在定义域中的变量,函数则不能:
fn main(){ let x = 4; let equal_to_x = |z| z == x; let y = 4; assert!(equal_to_x(y)); }
编译可以通过。本质上闭包通过将环境中的变量保存起来来使用的,这种开销不被函数允许。
同样闭包可以以三种(所有权、动态引用、静态引用)方式获取环境,也对应三种(FnOnce/FnMut/Fn)Fn特性。编译器会根据具体情况实现这些特性。
也可以通过一些手段强制应用其中的一些特性,例如move能够将环境中的变量强制转移所有权:
let equal_to_x = move |z| z == x;
可以利用这种特性将所有权移交给其他线程。
迭代器
生成迭代器:
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
这个迭代器现在还不会完成任何工作。
使用迭代器:
for val in v1_iter { println!("Got: {}", val); }
迭代器特性及next方法
迭代器特性只需要实现一个next方法。next方法顾名思义指向下一个元素(包装在Some(&)里),没有返回None。
事实上不断调用next方法的过程即耗尽迭代器的过程。
iter迭代器返回静态引用,into_iter可以移交所有权,iter_mut返回动态引用。
消耗迭代器的方法
指sum方法,它会取用迭代器的所有权来求和。求和之后便不能再使用该迭代器。
#[test] fn iterator_sum() { let v1 = vec![1, 2, 3]; let v1_iter = v1.iter(); let total: i32 = v1_iter.sum(); //v1_iter无了 assert_eq!( total, 6); //通过编译 }
产生迭代器的方法
指map方法,通过闭包建立一一映射。
let v1: Vec<i32> = vec![1, 2, 3]; let v2: Vec<_> = v1.iter().map(|x| x+1).collect(); assert_eq!(v2, vec![2, 3, 4]);
使用闭包来捕获环境中的变量
指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(|s| s.size == shoe_size).collect()
//注意这里用了into_iter } #[cfg(test)] mod tests { use super::*; #[test] fn filters_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("boot"), }, ]; //环境 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("boot") }, ] ); } } fn main() {}
通过编译
通过迭代器特性实现自己的迭代器
声明->实现即可
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 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); }
通过迭代器改进程序
改的是上一章的程序,故略
效率
迭代器能在提供更高级抽象的同时不消耗多余时间。
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号