Hello Rust.
[TOC]
Rust 快速入门系列的第二篇。
闭包:可以捕获其所在环境的匿名函数。(C++类似lambda,可以从其定义的作用域捕获值)
闭包可以保存为变量,作为函数参数,或者函数返回值。
fn main(){
let closure = |x| x; // let closure = |x: String| -> String {x};
let s = closure(String::from("hello"));
// let n = closure(5); // error 上一句已经推断出closure中的具体类型
}
闭包不要求标注参数和返回值的类型;
闭包的定义,最终只会被推断出唯一的具体类型(或者显示标注类型)
每个闭包实例都对应一个唯一匿名类型,并且闭包都实现了以下Fn Trait之一:
Fn 不可变借用环境值
FnMut 可变借用环境值
FnOnce 取得所有权
use std::collections;
struct Cacher<T>
where
T: Fn(u32) -> u32,
{
calculation: T,
value: collections::HashMap<u32, u32>,
}
impl<T> Cacher<T>
where
T: Fn(u32) -> u32,
{
fn new(calculation: T) -> Cacher<T> {
Cacher {
calculation,
value: collections::HashMap::new(),
}
}
fn value(&mut self, arg: u32) -> u32 {
match self.value.get(&arg) {
Some(v) => *v,
None => {
let v = (self.calculation)(arg);
self.value.insert(arg, v);
v
}
}
}
}
【闭包还不支持泛型】
普通函数不能捕获上下文变量,而闭包可以。结合上述3种Fn Trait,Rust可以根据环境值的使用情况推断出具体的闭包类型:
默认只捕获值的不可变引用,实现 Fn (不可变借用,只能读)
获取值的可变引用,实现 FnMut(可变借用,可以修改值)
获取值的所有权,实现 FnOnce,只能调用一次(因为获取了所有权)
注意:3种Trait存在层级关系;实现了Fn的闭包都实现了FnMut,而实现了FnMut的闭包都实现了FnOnce,因此所有的闭包都实现了FnOnce.
使用move
关键字:
可以在定义闭包的参数列表前使用 move,强制闭包获取值的所有权。
将闭包传递给新线程以移动数据时,非常有用。
let x = vec![1, 2, 3];
let equal_to_x = move |z: i32| z == x[0];
// println!("{:#?}", x); // error [E0382]: use of moved value: `x`
对于基本类型以及引用类型,没有所有权的变量不影响。
Rust 迭代器是惰性迭代器,用于遍历:
let v = vec![1, 2, 3];
// for val in v
// 调用 into_iter() moved
for val:&i32 in v.iter() { // borrow
println!("{}", val);
}
实现:
迭代器都实现了trait Iterator
,需要实现next
方法;该 trait 也提供了一些默认实现方法。
pub trait Iterator{
type Item;
fn next(&mut self)->Option<Self::Item>;
// default methods elided
}
产生迭代器的迭代方法:
iter()
创建迭代器,用于迭代元素的不可变引用;
iter_mut()
创建迭代器,用于迭代元素的可变引用;
**into_iter()
**获取所有权,创建迭代器用于迭代元素本身;
Iterator trait 提供了迭代器适配器方法,用来将迭代器转换为不同种类的迭代器。
例如 map, filter
let v = vec![1, 2, 3];
let iter = v.iter().map(|x: &i32| x * 2);
let iter2 = v.iter().map(|x| x * 2).filter(|x| x > &3);
// 使用消耗型适配器方法(获取迭代器所有权,并会使用next方法)
// collect 收集元素到集合类型
let v2:Vec[i32] = iter.collect();
使用 zip 操作两个迭代器,转换为新的迭代器(元组类型)
遇到其中一个值是 None,迭代会停止。
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> {
self.count += 1;
if self.count < 6 {
Some(self.count)
} else {
None
}
}
}
let sum: u32 = Counter::new().take(5)
.zip(Counter::new().skip(1).take(4))
.map(|(a,b)| a*b)
.filter(|x| x%3==0)
.sum();
assert_eq!(18, sum);
Rust 中的引用(使用&
)就是指针类型,只借用指向的值,是最常见的指针类型。(切片也是引用类型,会有长度信息)
智能指针的行为类似指针,具有额外的元数据和功能;并且一般会拥有指向 数据的所有权
例如String | Vec<T>
,拥有一片内存区域以及元数据和相关功能。
智能指针,语义上应该实现Deref trait 和 Drop trait:
Deref Trait: 实现 deref() 方法
//允许智能指针 自定义解引用运算符 * 的行为
Drop Trait: 实现 drop() 方法
// 自定义当指针指针实例离开作用域时运行的代码
即实现了 Deref Trait (返回引用类型),使得实现该 trait 类型的变量可以使用*var
操作
*var
// <==>
*(var.deref())
std::mem::drop
函数;Deref tait 将&T ⇒ &U
并且类型 T 隐式实现了类型 U 的所有(不可变)方法。(隐式)解引用强制转换 deref coercion,实现了 Deref trait 的类型可以将其引用链式调用 deref 转换为其他类型的引用。
例如,String 实现了 Deref,可以自动将&String
转换为&str
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
impl<T> Deref for MyBox<T> {
type Target = T; // 关联类型,必须定义
fn deref(&self) -> &T {
&self.0
}
}
fn main(){
let m = MyBox::new(String::from("Rust"));
hello("hello Rust");
hello(&m);
hello(&String::from("Rust"));
hello(&(*m)[..])
}
DerefMut trait
来重载可变引用的*运算符
;Deref trait可以将可变引用可以转换为另一种类型不可变引用;DerefMut trait将可变引用可以转换为另一种类型的可变引用。
Box
最简单的智能指针,在堆内存上分配值,栈空间有指向数据的指针。其拥有数据所有权。
Rc
多重所有权的引用计数智能指针Rc(Reference counting),使得一份数据被多个所有者持有,引用计数为0才清理数据。通过不可变引用只提供对数据的不可变访问。
场景:
堆上数据被多个部分读取(不可变借用),但是编译器无法确定哪个部分最后使用完数据(否则将最后使用的部分设置为值的所有者即可);
只能用于单线程;
Rc::new()
用来生成对应类型的引用计数智能指针,使用Rc::clone(&rc)
来获取相同值的引用计数智能指针(引用计数+1)
use crate::List::{Cons, Nil};
use std::rc::Rc;
// List 中元素存活时间至少比 List 的生命周期相同
#[derive(Debug)]
enum List {
Cons(i32, Rc<List>),
Nil,
}
fn main() {
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
let b = Cons(3, Rc::clone(&a)); // 复制,增加引用计数
let c = Cons(4, Rc::clone(&a));
// print strong_count
println!("strong_count: {}", Rc::strong_count(&a)); // 获取强引用计数
println!("a = {:#?}", b);
println!("b = {:#?}", c);
}
RefCell
用于实现内部数据的可变性,允许你在只持有不可变引用的前提下对数据进行修改。
导入std::cell::RefCell
代表持有数据的唯一所有权,其可变性和借用规则是运行时期检查。
用于单线程环境
智能指针比较:
使用场景:需要在不可变环境中修改自身的数据,将数据使用RefCell<T>
包装,可以在运行期获取可变引用来修改数据。用于实现内部可变性。
获取内部值的不可变引用borrow()方法
,返回Ref<T>
其实现了 Deref
获取内部值的可变引用borrow_mut()方法
,返回RefMut<T>
其实现了 Deref
use std::cell::RefCell
struct MockMes{
sent_messages: RefCell<Vec<String>>,
}
impl MockMes{
fn send(&self, message: &str){
self.sent_messages.borrow_mut().push(String::from(message));
}
}
Ref<T> |RefMut<T>
(RefCell
RefCell 会记录这两种指针个数,来维护运行时的借用检查规则,给定时间只允许拥有多个不可变借用或者一个可变借用。
通常会将 Rc
使用 Weak
调用Rc::downgrade(&rc)
,返回Weak<T>
,并且弱引用计数weak_count
加1
调用Weak<T>
的 upgrade() 方法,返回Option<Rc<T>>
(需要保证指向的值仍然存在)
本文链接: Rust基础(二)
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
发布日期: 2022-05-30
最新构建: 2024-12-26
欢迎任何与文章内容相关并保持尊重的评论😊 !