今天是Rust精简核心笔记第三波,也是完结篇。之前已经介绍了二波,Rust精简核心笔记:第一波,深入浅出语法精华-CSDN博客,Rust精简核心笔记:第二波,语法精髓部分解锁-CSDN博客,通过三波分享把Rust的核心语法快速进行掌握,也方便进行知识点回顾和速查的左右。
这些精简语法虽可以快速的掌握核心知识点,但如果深入学习和应用还需要结合项目进行实战和练习(更多最新文章也可关注微信公号: 良技漫谈)
Rust精简笔记(三)
-
第三波主要包括函数&闭包&迭代器、指针&智能指针、引用模块的相关介绍
-
参考The Rust Programming Language & Rust in Action
十,函数、闭包、迭代器
函数:
-
函数的定义方式及在结构体实现里关联函数,关联函数与方法的使用区别
use std::primitive;
struct Point {
x: i32,
y: i32,
}
impl Point {
// 关联函数(没有self相关参数)
fn new(x: i32, y: i32) -> Point {
Point { x: x, y: y }
}
// 方法(参数为&self,是个隐示的,调用时无需传递表明是该类型而已)
fn get_x(&self) -> i32 {
self.x
}
}
fn main() {
//关联函数使用:: 方法使用类型.方法,如Point::new, point.get_x
let point = Point::new(5, 6);
println!("get x={}", point.get_x());
}
闭包:
-
**闭包(closures)**是可以保存在一个变量中或作为参数传递给其他函数的匿名函数。闭包的定义以一对竖线(|)开始,在竖线中指定闭包的参数
fn add_one_v1 (x: u32) -> u32 { x + 1 } //函数的定义
let add_one_v2 = |x: u32| -> u32 { x + 1 }; // 完整标注的闭包定义
let add_one_v3 = |x| { x + 1 }; // 闭包定义中省略了类型注解
let add_one_v4 = |x| x + 1 ; // 闭包体只有一行,去掉了大括号 -
闭包会捕获其环境:
-
可以捕获其环境并访问其被定义的作用域的变量。如下边 x 并不是 equal_to_x 的一个参数,equal_to_x 闭包也被允许使用变量 x,因为它与 equal_to_x 定义于相同的作用域
fn main() {
let x = 4;
let equal_to_x = |z| z == x;
let y = 4;assert!(equal_to_x(y));
}
-
当闭包从环境中捕获一个值,闭包会在闭包体中储存这个值以供使用,这会使用内存并产生额外的开销。
-
闭包可以通过三种方式捕获其环境,他们直接对应函数的三种获取参数的方式:获取所有权,可变借用和不可变借用。
FnOnce 消费从周围作用域捕获的变量,闭包周围的作用域被称为其 环境,environment。为了消费捕获到的变量,闭包必须获取其所有权并在定义闭包时将其移动进闭包。其名称的 Once 部分代表了闭包不能多次获取相同变量的所有权的事实,所以它只能被调用一次
FnMut 获取可变的借用值所以可以改变其环境
Fn 从其环境获取不可变的借用值
由于所有闭包都可以被调用至少一次,所以所有闭包都实现了 FnOnce .大部分需要指定一个 Fn 系列 trait bound 的时候,可以从 Fn 开始,而编译器会根据闭包体中的情况告诉你是否需要 FnMut 或 FnOnce。
-
带有泛型和 Fn trait 的闭包: 可以创建一个存放闭包和调用闭包结果的结构体, 目的:结构体只会在需要结果时执行闭包,并会缓存结果值,再次调用闭包可以复用该值.
struct Cacher<T>
where
T: Fn(u32) -> u32,
{
calculation: T,
value: Option<u32>,
}
创建Cache的结构体,泛型T类型使用where 声明类型为闭包,结构体包含一个闭包,和一个用于存放闭包返回的值的u32类型,因为有可能第一次没有缓存,所有使用Option 的类型。即可能是some(u32) 或者None
-
官方完整例子:
use std::thread;
use std::time::Duration;struct Cacher<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 } } }
}
fn generate_workout(intensity: u32, random_number: u32) {
let mut expensive_result = Cacher::new(|num| {
println!("calculating slowly...");
thread::sleep(Duration::from_secs(2));
num
});if intensity < 25 { println!("Today, do {} pushups!", expensive_result.value(intensity)); println!("Next, do {} situps!", expensive_result.value(intensity)); } else { if random_number == 3 { println!("Take a break today! Remember to stay hydrated!"); } else { println!( "Today, run for {} minutes!", expensive_result.value(intensity) ); } }
}
fn main() {
let simulated_user_specified_value = 10;
let simulated_random_number = 7;generate_workout(simulated_user_specified_value, simulated_random_number);
}
a.这样可以起到了使用结构体缓存了闭包执行的结果,会先从结构体里查找缓存的值,没有再计算。b.同理也可以改造value的类型为HashMap, 可以通过key来找值,避免返回之前计算的始终同一个值。
iterator:
-
迭代器(iterator): 负责遍历序列中的每一项和决定序列何时结束的逻辑。
let v1 = vec![1, 2, 3]; let v1_iter = v1.iter(); let total: i32 = v1_iter.sum(); println!("value = {}", { total })
-
next 是 Iterator 实现者被要求定义的唯一方法
let v1 = vec![1, 2, 3]; let mut v1_iter = v1.iter(); assert_eq!(v1_iter.next(), Some(&1));
-
调用 map 方法创建一个新迭代器,接着调用 collect 方法消费新迭代器并创建一个 vector
//next 一次返回迭代器中的一个项,封装在 Some 中,当迭代器结束时,它返回 None
fn main() {
let v1: Vec<i32> = vec![1, 2, 3];
let mut newiter = v1.iter().map(|x| x + 1);
let newVector: Vec<_> = newiter.collect();
assert_eq!(newVector, vec![2, 3, 4]);
} -
迭代器 iter()、iter_mut()、into_iter()区别:
-
iter()返回的是值的不可变引用. 即&T
-
iter_mut() 返回的是值的可变引用. 即&mut T
-
into_iter() 返回的是T类型的值
use core::num;
fn main() {
// iter() 返回的是值的不可变引用,即&T.(此处map里闭包x本身无法改变)
let vec = vec![1, 2, 3, 4];
let new_vec: Vec<_> = vec.iter().map(|x| x + 1).collect();
println!("{:?}", vec);
println!("{:?}", new_vec);//iter_mut() 返回的是值的可变引用,即&mut T.(此处map里闭包x本身+1) let mut vec = vec![1, 2, 3, 4]; vec.iter_mut().for_each(|x| *x += 1); println!("{:?}", vec); //into_iter() 返回的是T类型的值 (因为所有权 vec是不能再使用) let vec = vec![1, 2, 3, 4]; let new_vec: Vec<_> = vec.into_iter().filter(|x| *x == 2).collect(); // println!("{:?}", vec); // 无法编译 println!("{:?}", new_vec);
}
-
实现Iterator trait 来创建自定义迭代器:
struct Counter {
count: u32,
}impl Counter {
fn new() -> Counter {
Counter { count: 0 }
}
}//Counter 类型实现 Iterator trait,通过定义 next 方法来指定使用迭代器时的行为
impl Iterator for Counter {
type Item = u32;
//将迭代器的关联类型 Item 设置为 u32,意味着迭代器会返回 u32 值集合
fn next(&mut self) -> OptionSelf::Item {
if self.count < 5 {
self.count += 1;
Some(self.count)
} else {
None
}
}
} -
Rust里iterator的定义:
pub trait Iterator {
type Item;fn next(&mut self) -> Option<Self::Item>;
}
十一,指针&智能指针
-
指针是一个包含内存地址的变量的通用概念, 智能指针(smart pointers)是一类数据结构,他们的表现类似指针,但是也拥有额外的元数据和功能
-
智能指针通常使用结构体实现,智能指针其实现了 Deref 和 Drop trait(离开作用域时运行的代码)
1. Box 用于在堆上分配值:
let b = Box::new(1);
2. Rc 引用计数智能指针:
-
Rc
只能用于单线程场景
//Rc::clone 只会增加引用计数, 这样a,b都是指向1 let a = Rc::new(1); let b = Rc::clone(&a);
3. RefCell<T> 和内部可变性模式:
-
RefCell<T> 代表其数据的唯一的所有权, 他具有如下特点:
// 在任意给定时刻,只能拥有一个可变引用或任意数量的不可变引用 之一(而不是两者)。
//引用必须总是有效的。let num = 1; let r1 = RefCell::new(1); // Ref - 只有一个不可变借用 let r2 = r1.borrow(); // RefMut - mutable 可变借用 let r3 = r1.borrow_mut(); // RefMut - 可变借用 let r4 = r1.borrow_mut();
-
内部可变性(Interior mutability): 是Rust 中的一个设计模式,它允许你即使在有不可变引用时也可以改变数据。
-
实现是通过不可变的Rc<T>, 此时的T的类型为RefCell<T>, 即结合成Rc<RefCell<T>> 来实现内部可变性,而外部是无法修改的。
-
let value = Rc::new(RefCell::new(5)) 完整例子如下:
#[derive(Debug)]
enum List {
Cons(Rc<RefCell<i32>>, Rc<List>),
Nil,
}use crate::List::{Cons, Nil};
use std::cell::RefCell;
use std::rc::Rc;fn main() {
let value = Rc::new(RefCell::new(5));let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil))); let b = Cons(Rc::new(RefCell::new(3)), Rc::clone(&a)); let c = Cons(Rc::new(RefCell::new(4)), Rc::clone(&a)); *value.borrow_mut() += 10; println!("a after = {:?}", a); println!("b after = {:?}", b); println!("c after = {:?}", c);
}
十二,使用和引用模块代码:
-
模块的创建和引用
fn some_function() {}
mod outer_module {
// private module
pub mod inner_module {
// public module
pub fn inner_public_function() {
super::super::some_function();
}
fn inner_private_function() {}
}
}
fn main() {
// 绝对路径 从 crate 根开始,以 crate 名或者字面值 crate 开头。
crate::outer_module::inner_module::inner_public_function();
// 相对路径(relative path)从当前模块开始,以 self、super 或当前模块的标识符开头。
outer_module::inner_module::inner_public_function();
// 使用 use 关键字将路径引入作用域
use outer_module::inner_module;
inner_module::inner_public_function();
}
连续三波的精简笔记整理完了,这些是比较好的快速学习rust和日常回顾的资料,但这些还只是rust基础。
想要把rust理解和运用熟练,和其他编程语言一样,先写上几万行代码,做几个项目。
PS: 也欢迎大家评论和交流~ 更多文章也可关注微信公号: 良技漫谈