Skip to main content

Rust Book Datastructure - life-time

Rust
KIGA
Author
KIGA
This is a personal blog, intended for sharing.
Table of Contents

生命周期约束 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);
}