跳转至

Rust 标准库

1 枚举

1.1 Option

`Option` 是一个枚举,和C++17中新增的`optional`功能一致,通常用在函数可能返回空值的情况。

Option 定义如下,其中 T 是泛型参数,Some(T) 表示该枚举成员的数据类型是 T,换句话说,Some 可以包含任何类型的数据。

enum Option<T> {
    Some(T),
    None,
}

需要注意的几点如下: - 无需使用 Option:: 前缀就可直接使用 Some 和 None。 - 当使用 None 是需要显示指定类型 T,如: Option<i8>, 当使用 Some 会自动推导 - Option 值不能直接使用,需要 unwrap() 函数获取值。

#![allow(unused)]
fn main() {
    let x: i8 = 5;
    let y = Some(5);
    let z:Option<i8> = None;
    // unwrap()获取值,不能直接使用y
    let sum = x + y.unwrap();
    println!("{}",sum);
}
fn divide(numerator: f64, denominator: f64) -> Option<f64> {
    if denominator == 0.0 {
        None
    } else {
        Some(numerator / denominator)
    }
}

// 函数的返回值是一个选项
let result = divide(2.0, 3.0);

// 模式匹配以获取值
match result {
    // 该划分有效
    Some(x) => println!("Result: {x}"),
    // 划分无效
    None    => println!("Cannot divide by 0"),
}

1.1.1 问号运算符,?

与 Result 类型类似,在编写调用许多返回 Option 类型的函数的代码时,处理 Some/None 可能会很乏味。问号运算符 ? 隐藏了一些在调用栈上传播值的样板。

它将替换为:

fn add_last_numbers(stack: &mut Vec<i32>) -> Option<i32> {
    let a = stack.pop();
    let b = stack.pop();

    match (a, b) {
        (Some(x), Some(y)) => Some(x + y),
        _ => None,
    }
}

有了这个:

fn add_last_numbers(stack: &mut Vec<i32>) -> Option<i32> {
    Some(stack.pop()? + stack.pop()?)
}

好多了!

以 ? 结尾的表达式将导致 Some 的展开值,除非结果为 None,在这种情况下,None 会从封闭的函数中提前返回。

? 可以用在返回 Option 的函数中,因为它提供了 None 的提前返回。

fn main() {
    fn add_last_numbers(stack: &mut Vec<i32>) -> Option<i32> {
        Some(stack.pop()? + stack.pop()?)
    }
    let mut v1: Vec<i32> = vec![1, 2, 3];
    let mut result = add_last_numbers(&mut v1);
    print!("First: ");
    match result {
        Some(x) => println!("value is {x}"),
        None => println!("None"),
    }
    result = add_last_numbers(&mut v1);
    print!("Second: ");
    match result {
        Some(x) => println!("value is {x}"),
        None => println!("None"),
    }
}


// 输出结果
// First: value is 5
// Second: None

1.2 Result

`Result`通常用于函数执行返回成功或者失败的场景。

Result 定义如下:

enum Result<T, E> {
    Ok(T),
    Err(E),
}

Result 的一些转换方法

```rust#![allow(unused)] fn main() { let good_result: Result = Ok(10); let bad_result: Result = Err(10);

// `is_ok` 和 `is_err` 方法按照他们说的做。
assert!(good_result.is_ok() && !good_result.is_err());
assert!(bad_result.is_err() && !bad_result.is_ok());

// `map` 消耗 `Result` 并产生另一个。
let good_result: Result<i32, i32> = good_result.map(|i| i + 1);
let bad_result: Result<i32, i32> = bad_result.map(|i| i - 1);
// 使用 `and_then` 继续计算。
let good_result: Result<bool, i32> = good_result.and_then(|i| Ok(i == 11));

// 使用 `or_else` 处理该错误。
let bad_result: Result<i32, i32> = bad_result.or_else(|i| Ok(i + 20));

// 消费结果并用 `unwrap` 返回内容。
println!("bad_result={}", bad_result.unwrap());
println!("good_result={}", good_result.unwrap());

}

// 输出结果 // bad_result=30 // good_result=true

## 2 类型
### 2.1 Any
### 2.2 String
## 3 智能指针
### 3.1 Box

```ad-info
Box 是一种从堆上分配内存的方式,相当于 C++中的智能指针,超过作用域自动释放分配的内存,也可以用`*`来解引用,还可以通过`.`来访问结构中的成员。

语法:

pub struct Box<T, A = Global>(_, _)
where
         A: Allocator,
         T: ?Sized;

示例:

#![allow(unused)]
fn main() {
    let mut boxed_i32 = Box::new(5_i32);
    // 解引用后赋值
    *boxed_i32 = 3;
    println!("{boxed_i32:?}");
    #[derive(Debug)]
    struct Point {
        x: i32,
        y: i32,
    }
    let mut pt = Box::new(Point { x: 1, y: 2 });
    // 修改结构体中的数据
    pt.x = 2;
    println!("{pt:?}");
}

// 输出结果
3
Point { x: 2, y: 2 }

3.1.1 解引用

fn main(){
    let mut i=Box::new(2);
    *i.as_mut()=3;
    println!("{i}");
    *i=4;
    println!("{i}");
}

// 输出
// 3
// 4

3.1.2 配合 Any

#![allow(unused)]
fn main() {
    use std::any::Any;

    fn print_if_string(value: Box<dyn Any>) {
        if let Ok(string) = value.downcast::<String>() {
            println!("String ({}): {}", string.len(), string);
        }
    }

    let my_string = "Hello World".to_string();
    print_if_string(Box::new(my_string));
    print_if_string(Box::new(0i8));
}

// 输出
// String (11): Hello World

3.1.3 into_raw、from_raw

  • 功能:into_raw 从 Box 转化成裸指针,from_raw 从裸指针转换成 Box。 实现:
pub fn into_raw(b: Self) -> *mut T {
    Self::into_raw_with_allocator(b).0
}
pub unsafe fn from_raw(raw: *mut T) -> Self {
     unsafe { Self::from_raw_in(raw, Global) }
}

示例

#![allow(unused)]
fn main() {
    let x: Box<String> = Box::new(String::from("Hello"));
    let ptr: *mut String = Box::into_raw(x);
    let x: Box<String> = unsafe { Box::from_raw(ptr) };
}

3.1.4 leak

  • 功能:消耗并泄漏 Box,返回一个可变引用,&'a mut T。 请注意,类型 T 必须超过所选的生命周期 'a。 如果类型仅具有静态引用,或者根本没有静态引用,则可以将其选择为 'static

该函数主要用于在程序的剩余生命期内保留的数据。 丢弃返回的引用将导致内存泄漏。 如果这是不可接受的,则应首先将引用与 Box::from_raw 函数包装在一起,生成 Box

这个 Box 可以被丢弃,这将正确销毁 T 并释放分配的内存。 实现:

pub fn leak<'a>(b: Self) -> &'a mut T
where
    A: 'a,
{
    unsafe { &mut *mem::ManuallyDrop::new(b).0.as_ptr() }
}

示例:

#![allow(unused)]
fn main() {
    let x = Box::new(41);
    let static_ref: &'static mut usize = Box::leak(x);
    *static_ref += 1;
    assert_eq!(*static_ref, 42);
    // Box::leak已经move了所有权,不能再访问x了
    // let y=*x;
}

3.2 Rc

https://www.rustwiki.org.cn/zh-CN/std/rc/index.html

Rust中Rc相当于C++中shared_ptr,Weak相当于C++中的weak_ptr。

单线程引用计数指针。 特点如下: - Rc<T> 类型提供了在堆中分配的 T 类型值的共享所有权。在 Rc 上调用 clone 会产生一个指向堆中相同分配的新指针。当指向给定分配的最后一个 Rc 指针被销毁时,存储在该分配中的值 (通常称为 “内部值”) 也将被丢弃。 - 默认情况下,Rust 中的共享引用不允许可变,Rc 也不例外:您通常无法获得 Rc 内部内容的可变引用。 如果需要可变性,则将 Cell 或 RefCell 放在 Rc 内; - Rc 使用非原子引用计数。 如果需要多线程的原子引用计数,请使用 sync::Arc。 - downgrade 方法可用于创建非所有者 Weak 指针。 Weak 指针可以被 upgrade 到 Rc,但是如果已经丢弃了分配中存储的值,则它将返回 None。 换句话说,Weak 指针不会使分配内部的值保持活动状态。但是,它们确实使分配 (内部值的后备存储) 保持活动状态。 - 为了避免 Rc 循环引用可以使用 Weak 用于中断循环。例如,一棵树可以具有从父节点到子节点的强 Rc 指针,以及从子节点到其父节点的 Weak 指针。 - Rc<T> 自动取消对 T 的引用 (通过 Dereftrait),因此您可以在类型为 Rc<T> 的值上调用 T 的方法。 为了避免与 T 方法的名称冲突,Rc<T> 本身的方法是关联函数,使用 完全限定语法 进行调用:

use std::rc::Rc;

let my_rc = Rc::new(());
let my_weak = Rc::downgrade(&my_rc);

RC<T> 也可以使用完全限定语法来调用 traits 的 Clone 等实现。有些人喜欢使用完全限定的语法,而另一些人则喜欢使用方法调用语法。

use std::rc::Rc;

let rc = Rc::new(());
// 方法调用语法
let rc2 = rc.clone();
// 完全限定的语法
let rc3 = Rc::clone(&rc);

3.2.1 Rc

单线程引用计数指针。Rc 代表引用计数。 实现:

pub struct Rc<T>
where
    T: ?Sized,
{ /* private fields */ }

示例

use std::rc::Rc;

struct Owner {
    name: String,
    // ...其他字段
}

struct Gadget {
    id: i32,
    owner: Rc<Owner>,
    // ...其他字段
}

fn main() {
    // 创建一个引用计数的 `Owner`。
    let gadget_owner: Rc<Owner> = Rc::new(
        Owner {
            name: "Gadget Man".to_string(),
        }
    );

    // 创建属于 `gadget_owner` 的 `Gadget`。
    // 克隆 `Rc<Owner>` 为我们提供了指向同一个 `Owner` 分配的新指针,从而增加了该进程中的引用计数。
    //
    let gadget1 = Gadget {
        id: 1,
        owner: Rc::clone(&gadget_owner),
    };
    let gadget2 = Gadget {
        id: 2,
        owner: Rc::clone(&gadget_owner),
    };

    // 处理我们的局部变量 `gadget_owner`。
    drop(gadget_owner);

    // 尽管丢弃了 `gadget_owner`,我们仍然可以打印出 `Gadget` 的 `Owner` 的名称。
    // 这是因为我们只删除了一个 `Rc<Owner>`,而不是它指向的 `Owner`。
    // 只要还有其他 `Rc<Owner>` 指向相同的 `Owner` 分配,它将保持活动状态。
    // 字段投影 `gadget1.owner.name` 之所以起作用,是因为 `Rc<Owner>` 自动取消了对 `Owner` 的引用。
    //
    //
    println!("Gadget {} owned by {}", gadget1.id, gadget1.owner.name);
    println!("Gadget {} owned by {}", gadget2.id, gadget2.owner.name);

    // 在该函数的末尾,`gadget1` 和 `gadget2` 被销毁,并且它们与我们的 `Owner` 一起被算作最后引用。
    // `Gadget` 现在也被摧毁。
    //
}

3.2.2 Weak

实现:

pub struct Weak<T>
where
    T: ?Sized,
{ /* private fields */ }
  • Weak 是 Rc 的一个版本,它持有对托管分配的非所有权引用。通过在 Weak 指针上调用 upgrade 来访问分配,它返回一个 Option<Rc<T>>
  • 由于 Weak 引用不计入所有权,因此它不会防止存储在分配中的值被丢弃,并且 Weak 本身不保证该值仍然存在。
  • 因此,当 upgrade 时,它可能返回 None。 但是请注意,Weak 引用 确实 会阻止分配本身 (后备存储) 被释放。
  • Weak 指针可用于保持对 Rc 管理的分配的临时引用,而又不会阻止其内部值被丢弃。 它也用于防止 Rc 指针之间的循环引用,因为相互拥有引用将永远不允许丢弃 Rc。 例如,一棵树可以具有从父节点到子节点的强 Rc指针,以及从子节点到其父节点的Weak` 指针。
  • 获取 Weak 指针的典型方法是调用 Rc::downgrade
  • Weak<T> 不会自动解引用到 T,因为内部值可能已经被丢弃了。

示例:

use std::rc::Rc;
use std::rc::Weak;
use std::cell::RefCell;

struct Owner {
    name: String,
    gadgets: RefCell<Vec<Weak<Gadget>>>,
    // ...其他字段
}

struct Gadget {
    id: i32,
    owner: Rc<Owner>,
    // ...其他字段
}

fn main() {
    // 创建一个引用计数的 `Owner`。
    // 请注意,我们已将 `Gadget` 的所有者的 vector 放在 `RefCell` 内,以便我们可以通过共享的引用对其进行可变。
    //
    let gadget_owner: Rc<Owner> = Rc::new(
        Owner {
            name: "Gadget Man".to_string(),
            gadgets: RefCell::new(vec![]),
        }
    );

    // 如前所述,创建属于 `gadget_owner` 的 `Gadget`。
    let gadget1 = Rc::new(
        Gadget {
            id: 1,
            owner: Rc::clone(&gadget_owner),
        }
    );
    let gadget2 = Rc::new(
        Gadget {
            id: 2,
            owner: Rc::clone(&gadget_owner),
        }
    );

    // 将 `Gadget` 添加到其 `Owner` 中。
    {
        let mut gadgets = gadget_owner.gadgets.borrow_mut();
        gadgets.push(Rc::downgrade(&gadget1));
        gadgets.push(Rc::downgrade(&gadget2));

        // `RefCell` 动态借用到此结束。
    }

    // 遍历我们的 `Gadget`,将其详细信息打印出来。
    for gadget_weak in gadget_owner.gadgets.borrow().iter() {

        // `gadget_weak` 是一个 `Weak<Gadget>`。
        // 由于 `Weak` 指针不能保证分配仍然存在,因此我们需要调用 `upgrade`,它返回 `Option<Rc<Gadget>>`。
        //
        //
        // 在这种情况下,我们知道分配仍然存在,因此我们只用 `unwrap` 和 `Option`。
        // 在更复杂的程序中,可能需要适当的错误处理才能获得 `None` 结果。
        //

        let gadget = gadget_weak.upgrade().unwrap();
        println!("Gadget {} owned by {}", gadget.id, gadget.owner.name);
    }

    // 在该函数的末尾,`gadget_owner`,`gadget1` 和 `gadget2` 被销毁。
    // 现在没有指向该 `Gadget` 的强大 (`Rc`) 指针,因此它们已被销毁。
    // 这会使 Gadget Man 的引用计数为零,因此他也被销毁了。
    //
}

3.3 Cell & RefCell & OnceCell

https://www.rustwiki.org.cn/zh-CN/std/cell/index.html# https://rustwiki.org/zh-CN/book/ch15-05-interior-mutability.html 可共享的可变容器。

Rust 内存安全基于以下规则:给定一个对象 T,它只能具有以下之一:

  • 对对象具有多个不可变引用 (&T) (也称为别名)。
  • 对对象有一个可变引用 (&mut T) (也称为可变性)。

这由 Rust 编译器强制执行。但是,在某些情况下,此规则不够灵活。有时需要对一个对象进行多次引用,然后对其进行可变的。

存在共享的可变容器以允许以受控的方式进行可变性,即使在出现混叠的情况下也是如此。 Cell<T>RefCell<T> 和 OnceCell<T> 允许以单线程方式实现这一点,但它们不实现 Sync。 (如果您需要在多个线程之间进行别名和可变的,Mutex<T>RwLock<T>OnceLock<T> 或 atomic 类型是执行此操作的正确数据结构)。

Cell<T>RefCell<T> 和 OnceCell<T> 类型的值可以通过共享引用 (即常见的 &T 类型) 进行可变,而大多数 Rust 类型只能通过唯一的 (&mut T) 引用进行可变。 我们说这些 cell 类型提供 内部可变性 (通过 &T 可变),与表现出 继承可变性 (仅通过 &mut T 可变) 的典型 Rust 类型形成对比。

Cell 类型分为三种类型: Cell<T>RefCell<T> 和 OnceCell<T>。每个都提供了一种不同的方式来提供安全的内部可变性。

3.3.1 Cell

Cell<T> 通过将值移入和移出 cell 来实现内部可变性。 也就是说,永远无法获取到内部值的 &mut T,如果不将其替换为其他内容,则无法直接获取值本身。 这两条规则都确保永远不会有一个以上的引用指向内部值。 该类型提供了以下方法:

  • 对于实现 Copy 的类型,get 方法通过复制它来检索当前内部值。
  • 对于实现 Default 的类型,take 方法将当前内部值替换为 Default::default(),然后返回替换后的值。
  • 所有类型都有:
    • replace: 替换当前内部值并返回替换后的值。
    • into_inner: 此方法使用 Cell<T> 并返回内部值。
    • set: 该方法替换内部值,丢弃替换后的值。

Cell<T> 通常用于更简单的类型,在这些类型中复制或移动值不会占用太多资源 (例如数字),并且在可能的情况下通常应优先于其他 cell 类型。 对于较大的非复制类型,RefCell 提供了一些优势。

3.3.2 RefCell

RefCell<T> 使用 Rust 的生命周期来实现 “dynamic borrowing”,这是一个可以临时、独占、可更改访问内部值的过程。 RefCell<T>s 的引用在运行时被跟踪,这与 Rust 的原生引用类型不同,后者在编译时完全静态地被跟踪。

可以使用 borrow 获取对 RefCell 的内部值 (&T) 的不可更改引用,使用 borrow_mut 可以获取不可更改引用 (&mut T)。 当调用这些函数时,它们首先验证是否满足 Rust 的借用规则: 允许任意数量的不可改变借用或允许单个不可改变借用,但绝不能同时使用。 如果尝试违反这些规则的借用,线程将崩溃。

RefCell<T> 对应的 Sync 版本为 RwLock<T>

3.3.2.1 在运行时记录借用

当创建不可变和可变引用时,我们分别使用 & 和 &mut 语法。对于 RefCell<T> 来说,则是 borrow 和 borrow_mut 方法,这属于 RefCell<T> 安全 API 的一部分。borrow 方法返回 Ref<T> 类型的智能指针,borrow_mut 方法返回 RefMut 类型的智能指针。这两个类型都实现了 Deref,所以可以当作常规引用对待。

RefCell<T> 记录当前有多少个活动的 Ref<T> 和 RefMut<T> 智能指针。每次调用 borrowRefCell<T> 将活动的不可变借用计数加一。当 Ref<T> 值离开作用域时,不可变借用计数减一。就像编译时借用规则一样,RefCell<T> 在任何时候只允许有多个不可变借用或一个可变借用。

如果我们尝试违反这些规则,相比引用时的编译时错误,RefCell<T> 的实现会在运行时出现 panic。

3.3.3 OnceCell

OnceCell<T> 在某种程度上是 Cell 和 RefCell 的混合体,适用于通常只需要设置一次的值。 这意味着无需移动或复制内部值 (与 Cell 不同) 也无需运行时检查 (与 RefCell 不同) 即可获得引用 &T。 然而,它的值一旦设置也不能更新,除非您有一个可变引用到 OnceCell

OnceCell 提供了以下方法:

  • get: 获取对内部值的引用
  • set: 如果未设置则设置内部值 (返回 Result)
  • get_or_init: 返回内部值,如果需要则初始化它
  • get_mut: 提供对内部值的可变引用,仅当您对 cell 本身具有可变引用时才可用。

OnceCell<T> 对应的 Sync 版本为 OnceLock<T>

4 时间 time

5 线程 thread

5.1 Arc

https://www.rustwiki.org.cn/zh-CN/std/sync/struct.Arc.html 线程安全的引用计数指针。Arc 代表原子引用计数。 实现:

pub struct Arc<T>
where
    T: ?Sized,
{ /* private fields */ }

Rust 中的共享引用不允许可变的,Arc 也不例外:您通常无法获得 Arc 内部内容的可变引用。如果需要通过 Arc 进行可变的,请使用 MutexRwLock 或 Atomic 类型之一。

在线程之间共享一些不可变数据:

use std::sync::Arc;
use std::thread;

let five = Arc::new(5);

for _ in 0..10 {
    let five = Arc::clone(&five);

    thread::spawn(move || {
        println!("{five:?}");
    });
}

共享可变的 AtomicUsize

use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;

let val = Arc::new(AtomicUsize::new(5));

for _ in 0..10 {
    let val = Arc::clone(&val);

    thread::spawn(move || {
        let v = val.fetch_add(1, Ordering::SeqCst);
        println!("{v:?}");
    });
}

6 内存 alloc

6.1 mem

https://rustwiki.org/zh-CN/std/alloc/index.html

6.2 Global

6.3 System