Skip to content

Rust 基础

基本概念

Rust Learning > Rust Crush Course > Design Patterns

内存所有权

String 的内存问题

变量在离开作用域后就被自动释放 将 String 赋值给其它变量,意味着从栈上拷贝了它的指针、长度和容量,并没有复制指针指向的堆上数据。 Rust 的操作方式称为移动而不是浅拷贝。

对于只在栈上的数据进行拷贝。如果一个类型实现了 Copy trait,在旧的变量赋值给其它变量后仍然可用。

向函数传递值,会使变量进入函数的作用域,在离开作用域时占用的内存会被释放。 函数的返回值也会转移所有权,将变量的值移出给调用的函数。

引用与借用

将对象的引用作为参数,而不是获取值的所有权。 &String s pointing at String s1

引用默认不可变,使用可变引用 &mut,对一个变量同时最多只能有一个可变引用;拥有不可变引用时不可创建可变引用。(重叠作用域)

切片类型

引用集合中一段连续的元素序列,而不用引用整个集合。

fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    &s[..]
}

泛型

struct Point<T> {
    x: T,
    y: T,
}

impl<T> Point<T> {
    fn x(&self) -> &T {
        &self.x
    }
}

Traits

定义特定类型可能与其它类型共享的功能。 类似于接口。

pub fn notify(item1: impl Summary, item2: impl Summary) {}

pub fn notify<T: Summary>(item1: T, item2: T) {}

通过 where 简化

fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U) -> i32 {}

fn some_function<T, U>(t: T, u: U) -> i32
    where T: Display + Clone,
          U: Clone + Debug
{}

生命周期

数据比引用有更长的生命周期,则是有效的引用。

生命周期标注表示多个引用的泛型生命周期参数如何相互关系。 不满足约束的值将被借用检查器拒绝。

#![allow(unused)]
fn main() {
    fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
        if x.len() > y.len() {
            x
        } else {
            y
        }
    }
}
// 返回值的生命周期必须与 x, y 一致

静态生命周期,能够存活于整个程序期间。

let s: &'static str = "I have a static lifetime.";

闭包

惰性求值,只在需要结果时执行闭包,并会缓存结果值

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
            },
        }
    }
}
// 改用 HashMap 以支持不同的 arg

智能指针

Treating Smart Pointers Like Regular References with the Deref Trait

引用计数,允许数据有多个所有者,记录总共由多少个所有者,当没有所有者时清理数据。智能指针相比引用,拥有它们指向的数据。

智能指针实现了 DerefDropDeref 使实例表现像引用一样,Drop 可以自定义离开作用域时的代码。

  • Box<T> 用于在堆上分配值
  • Rc<T> 引用计数类型,其数据可有多个所有者
  • Ref<T>RefMut<T>,只读引用和可变引用

Deref 允许重载解引用运算符 * Drop 运行清理代码

Rc<T> 通过不可变引用,在程序的多个部分之间只读地共享数据。

use std::rc::Rc;

let a = Rc::new();
let b = Rc::clone(&a);
let c = Rc::clone(&a);

println!("{}", Rc::strong_count(&a));

Rc::clone 只会增加引用计数,不会进行深拷贝。

RefCell<T> 能够在外部值被认为是不可变的情况下修改内部值

use std::cell::RefCell;

struct MockMessenger {
    sent_messages: RefCell<Vec<String>>,
}

impl MockMessagenger {
    fn new() -> MockMessenger {
        MockMessenger { sent_messages: RefCell::new(vec![])}
    }
}

impl Messenger for MockMessenger {
    fn send(&self, message: &str) {
        self.sent_messages.borrow_mut().push(String::from(message));
    }

}

避免引用循环:将 Rc<T> 变为 Weak<T> 调用 Rc::downgrade 会得到 Weak<T> 类型的智能指针。 弱引用并不属于所有权关系,弱引用的循环会在强引用计数为 0 时被打断。

为了使用指向的值,调用 Weak<T>upgrade 方法,返回 Option<Rc<T>>

异步

Rust Futures > Async Rust: What is a runtime? Here is how tokio works under the hood

Rust 并不提供用于执行 Futures 和 Streams 的上下文。如果不进行嵌入式开发,选择 tokio 即可。

异步的核心是 event loops 或称作 processors 每个 event-loop 有自己的任务队列,tokio 中的 processor 如果任务队列为空可以从其它 processor 处抢夺。

tokio::spawn(async move{
    for port in MOST_COMMON_PORTS_100 {
        let _ = input_tx.send(*port).await;
    }
});

可能导致阻塞的耗时任务 tokio::task::spawn_blocking (后台的无限循环任务应该使用 Thread)

let is_code_valid = spawn_blocking(move || crypto::verify_password(&code, &code_hash)).await?;

系统编程

Writing an OS in Rust

测试

单元测试

使用 #[cfg(test)] 标注只在 cargo test 才编译运行测试代码

pub fn add_two(a: i32) -> i32 {
    a + 2
}
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        assert_eq!(4, add_two(2));
    }
}

集成测试

在与 src 同级的 tests 文件夹下,每一个文件当作独立的 crate 来编译

use adder;

#[test]
fn it_adds_two() {
    assert_eq!(4, adder::add_two(2));
}

通过指定测试函数的名称作为 cargo test 的参数来运行特定集成测试 也可以指定运行 tests 目录下 integration_test.rs 中的所有测试 cargo test --test integration_test