Day 08 - 闭包 迭代器

本贴最后更新于 750 天前,其中的信息可能已经水流花落

image

img

📚 闭包

创建时间:2023-04-03 03:33 星期一


特点:

  • 是匿名函数
  • 可以保存为变量作为参数使用
  • 可以在一个地方创建闭包, 然后再另一个上下文中调用闭包完成运算
  • 可从其定义的作用域捕获值

闭包的定义

fn main(){ let lambda_print = |mut string:String| -> String{ println!("{}", &string); string.push_str("。") ; string // 也可以有返回值 }; let ret = lambda_print(String::from("你好阿姨")); println!("{}", ret); }

使用泛型参数和 Fn Trait 存储闭包

当我们需要对闭包函数的返回值进行存储以便下一次调用的时候直接使用返回值我们可以使用一个 struct 进行存储,这个模式通常叫做记忆化或者延迟计算

因为在泛型中因为存储某个具体的值需要知道这个值得类型。闭包也是一样的但是因为闭包的特殊性,即使是同一个签名的闭包其类型也是不同的,这个时候我们就需要使用特性和泛型进行操作了

所有的闭包都需要实现下列的特性之一:

  • fn trait
  • fnMut
  • fnOnce

struct RememberableCallback <T> where T: Fn(String)->String, { function_handler: T, arg: Option<String>, ret_cache: Option<String>, } impl<T> RememberableCallback<T> where T: Fn(String)->String, { fn new(callback: T) -> RememberableCallback<T>{ RememberableCallback { function_handler: callback, arg: Option::None, ret_cache: Option::None } } fn value(&mut self, arg:String) -> String{ match &self.ret_cache { Some(value) => { match &self.arg { None => { panic!("我不知道这种情况是怎么发生的"); }, Some(e) => { if e == &arg{ value.to_string() } else{ self.arg = Some((&arg).to_string()); let funcreturn = (self.function_handler)(arg); (self.ret_cache) = Some((&funcreturn).clone()); funcreturn } } } }, None => { self.arg = Some((&arg).to_string()); let funcreturn = (self.function_handler)(arg); (self.ret_cache) = Some((&funcreturn).clone()); funcreturn }, } } } fn main(){ let mut lambda_print = RememberableCallback::new( |mut string:String|{ string.push_str("。") ; string } ); let ret = lambda_print.value("我是天才".to_string()); let ret2 = lambda_print.value("谁是天才".to_string()); println!("{}", ret); println!("{}", ret2); }

此外闭包还可以访问他所定义的作用域中的变量

fn main() { let x = 3; let func_handle = || -> i32 {x} ; println!("{}", func_handle()); }

闭包获得参数的三种方式:

  • 【取得所有权】FnOnce
  • 【可变借用】FnMut
  • 【不可变借用】Fn

image

"捕获"是 Rust 中闭包的术语,指在闭包中获取外部变量的值或引用并将其保存到闭包内部。简单来说,这意味着一个函数可以在运行时“捕获”其中其他区域的状态。

当使用上下文移动语义(move semantics) 存储或传输(如将 closure 移动到另一个线程) 闭包对象时,从操作会复制整个环境,并且保存在堆上. 因此,在 move 之后不能再访问同样的环境拷贝,在相应变量上进行修改等操作也不被允许 。 这种方式确保了线程安全以及帮助预防竞态条件(race conditions) 和 锁(locking).

​​

fn main() { let x = vec![1,3,4,5]; let func_handle = move || -> Vec<i32> {x} ; println!("{:?}", func_handle()); println!("{}",x); }

当我们使用 move 的时候我们会强制获取闭包捕获的变量的作用域(拿来吧你)

img

📚 迭代器 跳转

创建时间:2023-04-04 02:21 星期二


迭代器的概念和 Python、Java 的迭代器类似

在 Rust 中所有迭代器都实现了 Iterator trait, 这个类有一个 next -> option 抽象方法.

创建迭代器的方法:

  • iter 【在不可变的引用上创建迭代器】
  • into_iter 【创建的迭代器会获得所有权】
  • iter_mut 【可变的引用】

消费迭代器的方法

Iterator​ trait 有一系列不同的由标准库提供默认实现的方法;你可以在 Iterator​ trait 的标准库 API 文档中找到所有这些方法。一些方法在其定义中调用了 next​ 方法,这也就是为什么在实现 Iterator​ trait 时要求实现 next​ 方法的原因。

这些调用 next​ 方法的方法被称为 消费适配器consuming adaptors),因为调用他们会消耗迭代器。一个消费适配器的例子是 sum​ 方法。这个方法获取迭代器的所有权并反复调用 next​ 来遍历迭代器,因而会消费迭代器。当其遍历每一个项时,它将每一个项加总到一个总和并在迭代完成时返回总和。示例 13-13 有一个展示 sum​ 方法使用的测试:

#[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); } }

调用 sum​ 之后不再允许使用 v1_iter​ 因为调用 sum​ 时它会获取迭代器的所有权。

产生其他迭代器的方法

Iterator​ trait 中定义了另一类方法,被称为 迭代器适配器iterator adaptors),他们允许我们将当前迭代器变为不同类型的迭代器。可以链式调用多个迭代器适配器。不过因为所有的迭代器都是惰性的,必须调用一个消费适配器方法以便获取迭代器适配器调用的结果。

示例 13-14 展示了一个调用迭代器适配器方法 map​ 的例子,该 map​ 方法使用闭包来调用每个元素以生成新的迭代器。这里的闭包创建了一个新的迭代器,对其中 vector 中的每个元素都被加 1。:

fn main() { let v1: Vec<i32> = vec![1, 2, 3]; v1.iter().map(|x| x + 1); }
  • Rust

    Rust 是一门赋予每个人构建可靠且高效软件能力的语言。Rust 由 Mozilla 开发,最早发布于 2014 年 9 月。

    58 引用 • 22 回帖 • 7 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...