生命周期约束 HRTB #
struct ImportantExcerpt<'a> {
part: &'a str,
}
// 将 &'a 类型的生命周期强行转换为 &'b 类型,会报错,只有在 'a >= 'b 的情况
// 下, 'a 才能转换成 'b
impl<'a: 'b, 'b> ImportantExcerpt<'a> {
fn announce_and_return_part(&'a self, announcement: &'b str) -> &'b str {
println!("Attention please: {}", announcement);
self.part
}
}
fn main() {
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().expect("Could not find a '.'");
let i = ImportantExcerpt {
part: first_sentence,
};
let s = i.announce_and_return_part("hello");
println!("{}", s);
}
Rust的生命周期和借用规则
// 'a: 'b表示'a的生命周期至少和'b一样长。
struct Interface<'b, 'a: 'b> {
manager: &'b mut Manager<'a>,
}
impl<'b, 'a: 'b> Interface<'b, 'a> {
pub fn noop(self) {
println!("interface consumed");
}
}
struct Manager<'a> {
// 字符串切片的引用。
text: &'a str,
}
struct List<'a> {
manager: Manager<'a>,
}
impl<'a> List<'a> {
// 接受self的可变引用,并返回一个Interface实例。'a: 'b表示'a的生命周期至少和'b一样长。
pub fn get_interface<'b>(&'b mut self) -> Interface<'b, 'a>
where
'a: 'b,
{
Interface {
manager: &mut self.manager,
}
}
}
// Interface的生命周期不需要和List一样长,所以在Interface被丢弃后,你仍然可以使用List。
fn main() {
// 创建一个List实例
let mut list = List {
manager: Manager { text: "hello" },
};
// 通过get_interface方法获取一个Interface实例,并调用其noop方法
list.get_interface().noop();
// 打印一条消息,表示Interface应该在这里被丢弃,借用被释放。
println!("Interface should be dropped here and the borrow released");
// 下面的调用可以通过,因为Interface的生命周期不需要跟list一样长
// use_list函数,接受一个List的引用,并打印manager字段的text字段。
use_list(&list);
}
fn use_list(list: &List) {
println!("{}", list.manager.text);
}
‘static 生命周期 #
一个引用必须要活得跟剩下的程序一样久,才能被标注为 &‘static 。
对于字符串字面量来说,它直接被打包到二进制文件中,永远不会被 drop ,因此它能跟程序活得一样久,自然它的生命周期是 ‘static
use std::fmt::Display;
fn main() {
let mark_twain: &str = "Samuel Clemens";
print_author(mark_twain);
let mark_twain = "Samuel Clemens";
print(&mark_twain);
}
fn print_author(author: &'static str) {
println!("{}", author);
}
// 'static 是作为生命周期约束来使用
fn print<T: Display + 'static>(message: &T) {
println!("{}", message);
}
use std::{slice::from_raw_parts, str::from_utf8_unchecked};
fn get_memory_location() -> (usize, usize) {
// “Hello World” 是字符串字面量,因此它的生命周期是 `'static`.
// 但持有它的变量 `string` 的生命周期就不一样了,它完全取决于变量作用域,对于该例子来说,也就是当前的函数范围
let string = "Hello World!";
let pointer = string.as_ptr() as usize;
let length = string.len();
(pointer, length)
// `string` 在这里被 drop 释放
// 虽然变量被释放,无法再被访问,但是数据依然还会继续存活
}
fn get_str_at_location(pointer: usize, length: usize) -> &'static str {
// 使用裸指针需要 `unsafe{}` 语句块
unsafe { from_utf8_unchecked(from_raw_parts(pointer as *const u8, length)) }
}
fn main() {
let (pointer, length) = get_memory_location();
let message = get_str_at_location(pointer, length);
println!(
"The {} bytes at 0x{:X} stored: {}",
length, pointer, message
);
// 如果大家想知道为何处理裸指针需要 `unsafe`,可以试着反注释以下代码
let message = get_str_at_location(1000, 10);
// Ran exercises/intro/intro.rs with errors,报错
// println!(
// "The {} bytes at 0x{:X} stored: {}",
// length, pointer, message
// );
}
结构体中的闭包 #
// T 的特征约束
struct Cacher<T>
where
// 闭包特征 Fn(u32) -> u32
T: Fn(u32) -> u32, // 对闭包形式进行了显而易见的限制
{
query: T, // query 是一个闭包. Fn(u32) -> u32 是一个特征,用来表示 T 是一个闭包类型
value: Option<u32>,
}
impl<T> Cacher<T>
where
T: Fn(u32) -> u32,
{
fn new(query: T) -> Cacher<T> {
Cacher { query, value: None }
}
// 先查询缓存值 `self.value`,若不存在,则调用 `query` 加载
fn value(&mut self, arg: u32) -> u32 {
match self.value {
Some(v) => v,
None => {
let v: u32 = (self.query)(arg);
self.value = Some(v);
v
}
}
}
}
fn main() {
let mut c: Cacher<impl Fn(u32) -> u32> = Cacher::new(|x| x);
let v1: u32 = c.value(1);
let v2: u32 = c.value(2);
println!("v1: {}, v2: {}", v1, v2);
}
fn fn_once<F>(func: F)
where
// func 的类型 F 实现了 Copy 特征,调用时使用的将是它的拷贝,并没有发生所有权的转移。
F: FnOnce(usize) -> bool + Copy,
{
println!("{}", func(3));
println!("{}", func(4));
}
fn main() {
let x = vec![1, 2, 3];
fn_once(|z| z == x.len())
}
use std::thread::JoinHandle;
// 强制闭包取得捕获变量的所有权,可以在参数列表前添加 move 关键字,这种用法通常用于
// 闭包的生命周期大于捕获变量的生命周期时,例如将闭包返回或移入其他线程。
fn get_owner() {
use std::thread;
let v: Vec<i32> = vec![1, 2, 3];
let handle: JoinHandle<()> = thread::spawn(move || {
println!("Here's a vector: {:?}", v);
});
handle.join().unwrap();
}
实际上,一个闭包并不仅仅实现某一种 Fn 特征,规则如下:
- 所有的闭包都自动实现了 FnOnce 特征,因此任何一个闭包都至少可以被调用一次
- 没有移出所捕获变量的所有权的闭包自动实现了 FnMut 特征
- 不需要对捕获变量进行改变的闭包自动实现了 Fn 特征
fn main() {
let mut s = String::new();
let update_string = |str| s.push_str(str);
exec(update_string);
println!("{:?}", s);
let update_string = move || println!("{}", s);
exec2(update_string);
let mut s = String::new();
let update_string = move || println!("{}", s);
exec3(update_string);
}
fn exec<'a, F: FnMut(&'a str)>(mut f: F) {
f("hello")
}
// 一个闭包实现了哪种 Fn 特征取决于该闭包如何使用被捕获的变量,而不是取决于闭包如何捕获它们
// move 本身强调的就是后者,闭包捕获变量
fn exec2<F: FnOnce()>(f: F) {
f()
}
// 闭包不仅仅实现了FnOnce 特征,还实现了 Fn 特征
fn exec3<F: Fn()>(f: F) {
f()
}
例
fn factory(x: i32) -> impl Fn(i32) -> i32 {
let num = 5;
if x > 1 {
move |x| x + num
} else {
move |x| x - num
}
}
error[E0308]: `if` and `else` have incompatible types
--> exercises/intro/intro.rs:33:9
|
30 | / if x > 1 {
31 | | move |x| x + num
| | ----------------
| | |
| | the expected closure
| | expected because of this
32 | | } else {
33 | | move |x| x - num
| | ^^^^^^^^^^^^^^^^ expected closure, found a different closure
34 | | }
| |_____- `if` and `else` have incompatible types
|
= note: expected closure `[closure@exercises/intro/intro.rs:31:9: 31:17]`
found closure `[closure@exercises/intro/intro.rs:33:9: 33:17]`
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object
修改后
fn factory(x: i32) -> Box<dyn Fn(i32) -> i32> {
let num = 5;
if x > 1 {
Box::new(move |x| x + num)
} else {
Box::new(move |x| x - num)
}
}
迭代器 #
fn main() {
let values = vec![1, 2, 3];
{
let result = match IntoIterator::into_iter(values) {
mut iter => loop {
match iter.next() {
Some(x) => {
println!("{}", x);
}
None => break,
}
},
};
result
}
// IntoIterator::into_iter 是使用完全限定的方式去调用 into_iter 方法,这种调用方式跟
// values.into_iter() 是等价的。
let arr = [1, 2, 3];
for v in arr.into_iter() {
println!("{}", v);
}
}
into_iter, iter, iter_mut #
- into_iter 会夺走所有权
- iter 是借用
- iter_mut 是可变借用
fn main() {
let values = vec![1, 2, 3];
for v in values.into_iter() {
println!("{}", v)
}
// 下面的代码将报错,因为 values 的所有权在上面 `for` 循环中已经被转移走
// println!("{:?}",values);
let values = vec![1, 2, 3];
let _values_iter = values.iter();
// 不会报错,因为 values_iter 只是借用了 values 中的元素
println!("{:?}", values);
let mut values = vec![1, 2, 3];
// 对 values 中的元素进行可变借用
let mut values_iter_mut = values.iter_mut();
// 取出第一个元素,并修改为0
if let Some(v) = values_iter_mut.next() {
*v = 0;
}
// 输出[0, 2, 3]
println!("{:?}", values);
}
迭代器消费 #
use std::collections::HashMap;
fn main() {
let names = ["sunface", "sunfei"];
let ages = [18, 18];
// 请把迭代器中的元素消费掉,然后把值收集成 Vec<_> 类型,_ 自动推导类型。
// zip 是一个迭代器适配器,它的作用就是将两个迭代器的内容压缩到一起,形成 Iterator<Item=(ValueFromA, ValueFromB)>
let folks: HashMap<_, _> = names.into_iter().zip(ages.into_iter()).collect();
println!("{:?}", folks);
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 是迭代器适配器,用于对迭代器中的每个值进行过滤。 它使用闭包作为参数,该闭包的参数
// s 是来自迭代器中的值,然后使用 s 跟外部环境中的 shoe_size 进行比较,若相等,则在迭代器中保留 s 值,
// 若不相等,则从迭代器中剔除 s 值,最终通过 collect 收集为 Vec<Shoe> 类型。
struct Shoe {
size: u32,
style: String,
}
fn shoes_in_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
shoes.into_iter().filter(|s| s.size == shoe_size).collect()
}
实现 Iterator 特征 #
- zip 把两个迭代器合并成一个迭代器,新迭代器中,每个元素都是一个元组,由之前两个迭代器的元素组成。例如将形如 [1, 2, 3, 4, 5] 和 [2, 3, 4, 5] 的迭代器合并后,新的迭代器形如 [(1, 2),(2, 3),(3, 4),(4, 5)]
- map 是将迭代器中的值经过映射后,转换成新的值[2, 6, 12, 20]
- filter 对迭代器中的元素进行过滤,若闭包返回 true 则保留元素[6, 12],反之剔除
- sum 是消费者适配器,对迭代器中的所有元素求和,最终返回一个 u32 值 18
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
}
}
}
fn main() {
let mut counter = Counter::new();
assert_eq!(counter.next(), Some(1));
assert_eq!(counter.next(), Some(2));
assert_eq!(counter.next(), Some(3));
assert_eq!(counter.next(), Some(4));
assert_eq!(counter.next(), Some(5));
assert_eq!(counter.next(), None);
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);
}
内存地址转为指针 #
fn main() {
let mut values: [i32; 2] = [1, 2];
let p1: *mut i32 = values.as_mut_ptr();
let first_address = p1 as usize; // 将p1内存地址转换为一个整数
let second_address = first_address + 4; // 4 == std::mem::size_of::<i32>(),i32类型占用4个字节,因此将内存地址 + 4
let p2 = second_address as *mut i32; // 访问该地址指向的下一个整数p2
unsafe {
*p2 += 1;
}
assert_eq!(values[1], 3);
}
类型转换错误
fn main() {
let b: i16 = 1500;
let b_: u8 = match b.try_into() {
Ok(b1) => b1,
Err(e) => {
println!("{:?}", e.to_string());
0
}
};
}
变形记(Transmutes) #
fn main() {
let pointer = foo as *const ();
let function = unsafe {
// 将裸指针转换为函数指针
std::mem::transmute::<*const (), fn() -> i32>(pointer)
};
assert_eq!(function(), 0);
struct R<'a>(&'a i32);
// 将 'b 生命周期延长至 'static 生命周期
unsafe fn extend_lifetime<'b>(r: R<'b>) -> R<'static> {
std::mem::transmute::<R<'b>, R<'static>>(r)
}
// 将 'static 生命周期缩短至 'c 生命周期
unsafe fn shorten_invariant_lifetime<'b, 'c>(r: &'b mut R<'static>) -> &'b mut R<'c> {
std::mem::transmute::<&'b mut R<'static>, &'b mut R<'c>>(r)
}
}
fn foo() -> i32 {
0
}
避免栈上数据的拷贝 #
fn main() {
// 在栈上创建一个长度为1000的数组
let arr = [0; 1000];
// 将arr所有权转移arr1,由于 `arr` 分配在栈上,因此这里实际上是直接重新深拷贝了一份数据
let arr1 = arr;
// arr 和 arr1 都拥有各自的栈上数组,因此不会报错
println!("{:?}", arr.len());
println!("{:?}", arr1.len());
// 在堆上创建一个长度为1000的数组,然后使用一个智能指针指向它
let arr = Box::new([0; 1000]);
// 将堆上数组的所有权转移给 arr1,由于数据在堆上,因此仅仅拷贝了智能指针的结构体,底层数据并没有被拷贝
// 所有权顺利转移给 arr1,arr 不再拥有所有权
let arr1 = arr;
println!("{:?}", arr1.len());
// 由于 arr 不再拥有底层数组的所有权,因此下面代码将报错
// println!("{:?}", arr.len());
let arr = vec![Box::new(1), Box::new(2)];
// 使用 & 借用数组中的元素,否则会报所有权错误
let (first, second) = (&arr[0], &arr[1]);
let sum = **first + **second;
println!("{}", sum);
}