跳过正文

rust-std

··30787 字
Rust Rust-Crate
目录
rust-crate - 这篇文章属于一个选集。

1 std::alloc
#

标准库使用一个 global 内存分配器来为 Box<T>/Vec<T> 等分配堆内存:

  1. 默认情况下,使用 std::alloc::System 作为 global,System 同时实现 了 Allocator 和 GlobalAlloc trait。
    • unix/linux:使用 malloc 实现。
    • windows:使用 HeapAlloc 实现。
  2. 用户可以使用 #[global_allocator] 来指定一个实现 std::alloc::GlobalAlloc trait 的自定义 global 内存分配器;

// 使用 OS 缺省的 System 内存分配器实现自定义内存分配器
use std::alloc::{GlobalAlloc, System, Layout};

struct MyAllocator;

unsafe impl GlobalAlloc for MyAllocator {
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
        System.alloc(layout)
    }

    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
        System.dealloc(ptr, layout)
    }
}

#[global_allocator]
static GLOBAL: MyAllocator = MyAllocator;
fn main() {
    let mut v = Vec::new(); // 使用 GLOBAL 内存分配器
    v.push(1);
}

// 使用 jemalloc 内存分配器
use jemallocator::Jemalloc;
#[global_allocator]
static GLOBAL: Jemalloc = Jemalloc; // Jemalloc 是 unit struct 类型
fn main() {}

std::alloc::Allocator trait 定义内存分配接口,根据传入的 std::alloc::Layout (如 align 要求和 size )来分配内存:

pub unsafe trait Allocator {
    // Required methods
    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout);
    //...
}

pub struct Layout { /* private fields */ }

impl Layout

// 创建 T 类型的 Layout
pub const fn new<T>() -> Layout
// 从 T 的 value 创建 Layout
pub fn for_value<T>(t: &T) -> Layout where T: ?Sized
pub unsafe fn for_value_raw<T>(t: *const T) -> Layout where T: ?Sized

pub const fn from_size_align(size: usize, align: usize ) -> Result<Layout, LayoutError>
pub const unsafe fn from_size_align_unchecked( size: usize, align: usize ) -> Layout

// 获得 Layout 的 size 和 align 信息
pub const fn size(&self) -> usize
pub const fn align(&self) -> usize

pub fn dangling(&self) -> NonNull<u8>
pub fn align_to(&self, align: usize) -> Result<Layout, LayoutError>
pub fn padding_needed_for(&self, align: usize) -> usize
pub fn pad_to_align(&self) -> Layout
pub fn repeat(&self, n: usize) -> Result<(Layout, usize), LayoutError>
pub fn extend(&self, next: Layout) -> Result<(Layout, usize), LayoutError>
pub fn repeat_packed(&self, n: usize) -> Result<Layout, LayoutError>
pub fn extend_packed(&self, next: Layout) -> Result<Layout, LayoutError>
pub fn array<T>(n: usize) -> Result<Layout, LayoutError>

内存分配函数(使用缺省的 Global 分配器,返回裸指针,如 *mut u8):

  • alloc : Allocate memory with the global allocator.
  • alloc_zeroed : Allocate zero-initialized memory with the global allocator.
  • dealloc : Deallocate memory with the global allocator.
  • handle_alloc_error : Signal a memory allocation error.
  • realloc : Reallocate memory with the global allocator.
  • set_alloc_error_hook : Registers a custom allocation error hook, replacing any that was previously registered.
  • take_alloc_error_hook : Unregisters the current allocation error hook, returning it.

这些函数没有返回错误,根据返回的 raw pointer 是否为 null 来判断是否分配成功:

  • handle_alloc_error(layout) 可以用来处理分配错误情况,默认的行为是在 stderr 打印消息并 abort 进程
  • set_alloc_error_hook() 和 take_alloc_error_hook() 设置调用 handle_alloc_error() 时的行为,可以选择 panic 或 abort;
// Allocate memory with the global allocator.
pub unsafe fn alloc(layout: Layout) -> *mut u8

use std::alloc::{alloc, dealloc, handle_alloc_error, Layout};
unsafe {
    let layout = Layout::new::<u16>();
    let ptr = alloc(layout); // ptr 是 *mut u8 类型
    if ptr.is_null() {
        handle_alloc_error(layout);
    }

    *(ptr as *mut u16) = 42;
    assert_eq!(*(ptr as *mut u16), 42);

    dealloc(ptr, layout);
}

// Allocate zero-initialized memory with the global allocator.
pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8

use std::alloc::{alloc_zeroed, dealloc, Layout};

unsafe {
    let layout = Layout::new::<u16>();
    let ptr = alloc_zeroed(layout);

    assert_eq!(*(ptr as *mut u16), 0);

    dealloc(ptr, layout);
}

// Deallocate memory with the global allocator.
pub unsafe fn dealloc(ptr: *mut u8, layout: Layout)

// Reallocate memory with the global allocator.
pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8

// Registers a custom allocation error hook, replacing any that was previously registered.
pub fn set_alloc_error_hook(hook: fn(_: Layout))
#![feature(alloc_error_hook)]

use std::alloc::{Layout, set_alloc_error_hook};

fn custom_alloc_error_hook(layout: Layout) {
    panic!("memory allocation of {} bytes failed", layout.size());
}

set_alloc_error_hook(custom_alloc_error_hook);

2 std::any
#

std::any module 提供了返回类型或值名称的函数(type 反射):

  1. type_name::<T>() : 返回 T 的类型名称字符串;
  2. type_name_of_value(&T) : 返回 T 类型名称字符串(必须传入 &T);
assert_eq!(std::any::type_name::<Option<String>>(),
    "core::option::Option<alloc::string::String>",);

use std::any::type_name_of_val;
let s = "foo";
let x: i32 = 1;
let y: f32 = 1.0;
assert!(type_name_of_val(&s).contains("str"));
assert!(type_name_of_val(&x).contains("i32"));
assert!(type_name_of_val(&y).contains("f32"));

std::any::Any trait : Rust 为 绝大部分类型实现了该 trait 。所以对象借用可以转换为 &dyn Any ,对象自身可以转换为 Box<dyn Any> 。但是对于借用类型,需要满足 'static 才能实现 Any trait。

// Any trait 需要满足 'static 限制
pub trait Any: 'static {
    fn type_id(&self) -> TypeId;
}

use std::any::{Any, TypeId};
fn is_string(s: &dyn Any) -> bool {
    TypeId::of::<String>() == s.type_id()
}

// 传入的是 'static 引用,自动实现了 Any trait
assert_eq!(is_string(&0), false);
assert_eq!(is_string(&"cookie monster".to_string()), true);

Any trait type_id() 方法返回 TypeId 类型对象,表示该 trait object 的具体类型,可以用来比较/Hash 和显示:


use std::any::{Any, TypeId};
fn is_string<T: ?Sized + Any>(_s: &T) -> bool {
    // of() 函数返回类型的 TypeId
    TypeId::of::<String>() == TypeId::of::<T>()
}
assert_eq!(is_string(&0), false);
assert_eq!(is_string(&"cookie monster".to_string()), true);

use std::any::{Any, TypeId};
let boxed: Box<dyn Any> = Box::new(3_i32);

// You're more likely to want this:
let actual_id = (&*boxed).type_id();
// ... than this:
let boxed_id = boxed.type_id();

assert_eq!(actual_id, TypeId::of::<i32>());
assert_eq!(boxed_id, TypeId::of::<Box<dyn Any>>());

&dyn Any 实现了 is<T>()/downcast_ref<T>()/downcast_mut<T>() 方法,可以对 trait object 进行动态类型判断和处理。T 必须是具体类型,而不能是 trait 类型。

use std::any::Any;

fn is_string(s: &dyn Any) {
    if s.is::<String>() {
        println!("It's a string!");
    } else {
        println!("Not a string...");
    }
}
is_string(&0);
is_string(&"cookie monster".to_string());

fn modify_if_u32(s: &mut dyn Any) {
    if let Some(num) = s.downcast_mut::<u32>() {
        *num = 42;
    }
}
let mut x = 10u32;
let mut s = "starlord".to_string();
modify_if_u32(&mut x);
modify_if_u32(&mut s);
assert_eq!(x, 42);
assert_eq!(&s, "starlord");

三个 Box 类型 trait object 都实现了 downcast<T> 和 downcast_unchecked<T> 方法:

  • impl<A> Box<dyn Any, A>
  • impl<A> Box<dyn Any + Send, A>
  • impl<A> Box<dyn Any + Sync + Send, A>
pub fn downcast<T>(self) -> Result<Box<T, A>, Box<dyn Any, A>> where T: Any
pub unsafe fn downcast_unchecked<T>(self) -> Box<T, A> where T: Any

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));

#![feature(downcast_unchecked)]
use std::any::Any;
let x: Box<dyn Any> = Box::new(1_usize);
unsafe {
    assert_eq!(*x.downcast_unchecked::<usize>(), 1);
}

3 std::backtrace
#

捕获和打印线程的调用栈,程序二进制需要包含 debug information,否则打印的 backtrace 不显示 filename/line number。

捕获调用栈有一定的性能开销,默认是关闭的。

Rust 1.77.0 版本开始,release 默认开启了 strip, 故不包含 debug info

Backtrace::capture() 的行为受两个环境变量控制,需要至少设置一个环境变量为非 0 值才会捕获调用栈,否则是关闭的:

  1. RUST_LIB_BACKTRACE - if this is set to 0 then Backtrace::capture will never capture a backtrace. Any other value set will enable Backtrace::capture.
  2. RUST_BACKTRACE - if RUST_LIB_BACKTRACE is not set, then this variable is consulted with the same rules of RUST_LIB_BACKTRACE.
impl Backtrace

pub fn capture() -> Backtrace // capture 受环境变量调控
pub fn force_capture() -> Backtrace // 强制捕获
pub const fn disabled() -> Backtrace // 关闭捕获
pub fn status(&self) -> BacktraceStatus  // 返回是否开启了 Backtrace 捕获

impl<'a> Backtrace
// BacktraceFrame 实现了 Debug,用于打印调用栈。
pub fn frames(&'a self) -> &'a [BacktraceFrame]

// 示例:Backtrace::force_capture() 函数不参考上面两个环境变量的值,强制返回
// Backtrace:
use std::backtrace::Backtrace
fn main() {
    let bt  = Backtrace::force_capture();
    println!("{}", bt); // Backtrace 实现了 Debug 和 Display,打印调用栈。
}

4 std::boxed
#

std::boxed module 提供了堆内存分配的 Box<T> 类型,Box 拥有分配内存的所有权,当 Box 离开 scope 时 drop 对应的内存。

std::boxed::Box 默认使用 Global 内存分配器在堆上分配内存, 由于泛型参数 A 有默认值, 所以一般只需要指定 T 类型, 如 Box<u8>

使用 Box 在堆上分配内存可以避免栈上大内存拷贝。Box<T> 大小是编译时已知的,可以保存 trait object 对象以及实现自引用类型(如树形递归数据结构):

pub struct Box<T, A = Global>(/* private fields */)
where
    A: Allocator,
    T: ?Sized;

let val: u8 = 5; // 栈变量,栈上分配内存
let boxed: Box<u8> = Box::new(val); // 堆上分配内存

// 使用 Box 保存递归数据类型对象
#[allow(dead_code)]
#[derive(Debug)]
enum List<T> {
    Cons(T, Box<List<T>>),
    Nil,
}
let list: List<i32> = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))));
println!("{list:?}");

// 使用 Box 保存 trait object
let _b: Box<dyn std::fmt::Display> = Box::new(123i32);
let _b: Box<&dyn std::fmt::Display> = Box::new(&123i32);

// 1i32 实现了 dyn std::fmt::Display 和 dyn std::any::Any, 所以 1i32 可以
// unsized coercion 到 trait object
//
// trait object 有两种形式:
// 1. &dyn Trait, &(dyn Trait + 'static + Send + Sync),使用对象引用赋值。
let dsd: &dyn std::fmt::Display = &1i32;

// 2. Box<dyn Trait>,使用对象赋值。
let bo: Box<dyn std::fmt::Display> = Box::new(1i32);
let bo: Box<dyn std::any::Any> = Box::new(1i32);
let bo: Box<&dyn std::fmt::Display> = Box::new(&1i32);

// 以下是 CoerceUnsized trait 实现的 type coercion 的转换, 将 &T 隐式转换为 &U
// 指针, U 需要是 unsized type.
//
// Rust 支持 Box<T> -> Box<U>, 但需要 T 可以 CoerceUnsized 到 U
let b1: Box<i32> = Box::new(123i32);
let b2: Box<dyn std::fmt::Display> = b1;
let bo: Box<[i32]> = Box::new([1, 2, 3]); // [T; N] -> [T]
let b1 = Box::new(123i32); // i32 -> dyn std::fmt::Display
let b2: Box<dyn std::fmt::Display> = b1;

Box<T> 没有实现 Copy,赋值时会被移动,其它智能指针 Rc/Arc/Cell/RefCell 类似。

use std::mem;
#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
struct Point {
    x: f64,
    y: f64,
}

#[allow(dead_code)]
struct Rectangle {
    top_left: Point,
    bottom_right: Point,
}

fn origin() -> Point {
    Point { x: 0.0, y: 0.0 }
}

fn boxed_origin() -> Box<Point> {
    // 在堆上分配 Point
    Box::new(Point { x: 0.0, y: 0.0 })
}

enum List {
    // 元组类型,由于 Box 没有实现 Copy,所以整个元组没有实现 Copy
    Cons(i32, Box<List>),
    Nil,
}

use crate::List::{Cons, Nil};

fn main() {
    let a = Cons(5, Box::new(Cons(10, Box::new(Nil))));
    // a 所有权转移。这里不能传入 &a, 否则只是分配一个指针的空间。
    let b = Cons(3, Box::new(a));
    // 编译器报错,解决办法是使用 Rc<T>
    // let c = Cons(4, Box::new(a));
}

Box<T> 是实现了 Deref<Target=T> 的智能指针,自动解引用和类型转换:

  1. 支持 * 运算符来解引用, *v 等效于 *(v.deref()) ,返回 T 值 , &*v 来返回 &T 值
  2. 在需要 &T 的地方可以直接传入 &Box<T> 值;
let boxed: Box<u8> = Box::new(5);
let val: u8 = *boxed;

fn hello(name: &str) {
    println!("Hello, {name}!");
}

fn main() {
    let x = 5;
    let y = Box::new(x);
    assert_eq!(5, *y); // *y 等效为 *(y.deref()), 返回 5。

    let boxed: Box<u8> = Box::new(5);
    let val: u8 = *boxed;

    let m = MyBox::new(String::from("Rust"));
    // Rust 自动进行多级 Defer 解引用,也就是 deref coercion:
    //   MyBox<String> -> String -> str
    hello(&m);

    // 如果 Rust 不做 deref coercion,则需要做如下繁琐操作
    hello(&(*m)[..]);
}

只要 T: Sized, 则 Box<T> 将确保可以用一个 single pointer 来代表, 和 C 指针(T*)是 ABI 兼容的=。这意味着 =如果要在 C 中调用 Rust func ,可以在 Rust func 中使用 Box<T> 类型,而在 C side 当作 T* 来使用。但是如果在 Rust func 中调用 C func,则不建议对 C 指针使用 Box<T> 类型,否则是未定义行为。而是尽可能使用 raw pointer;

例如,Rust 实现的,供 C 调用的函数签名 的 header:

/* C header */

/* Returns ownership to the caller */
struct Foo* foo_new(void);

/* Takes ownership from the caller; no-op when invoked with null */
void foo_delete(struct Foo*);

这两个 C 函数的 Rust 实现如下:

#[repr(C)]
pub struct Foo;

#[no_mangle]
pub extern "C" fn foo_new() -> Box<Foo> {
    Box::new(Foo)
}

// foo_delete() 的参数可能是 nullable 指针,而 Box<Foo> 不可能是 null,故需要使
// 用 Option
#[no_mangle]
pub extern "C" fn foo_delete(_: Option<Box<Foo>>) {}

也可以从 raw pointer 创建 Box 对象:

let value = &mut 32 as *mut i32;
let box = Box::<i32>::from_raw(value);
let value2 = &*box as *const i32;

5 std::cell
#

Rust 对象只能有一个所有者,对象借用需要满足借用规则,如不能通过共享借用 &T 修改 T 值。

在不可变对象中引入可变性,称为 内部可变性(interior mutability)

std::cell module 提供了 Cell<T>RefCell<T> 来支持这种场景:在对 Cell 自身没有 &mut 的情况下,也能修改它的值。

std::cell module 提供了多种内部可变性类型, 可以通过 &T 来修改引用的对象:

  • Cell<T>: T 必须实现 Copy;
  • RefCell<T>: T 不需要实现 Copy,如果不满足规则会导致运行时 panic;
  • OnceCell<T>: 对 T 只进行一次初始化。(不支持多线程,多线程版本是 OnceLock);
  • LazyCell<T>: 和 OnceCell 类似,但是通过 Deref 机制自动初始化一次。 (不支持多线程,多线程版本是 LazyLock);

各种 Cell 都没有实现 Sync,只能在单线程环境中使用。在多线程环境中, Mutex<T>, RwLock<T>, OnceLock<T>, LazyLock<T> 和各种 automic type 实现了多线程的内部可变性。

Cell<T> 要求 T 类型实现 Copy trait:

  1. Cell::new(value):转移 value 对象的所有权;
  2. cell.get():使用对象的 Copy trait 来返回 value;
  3. cell.set(&self, value: T):将 value 保存到 Cell 中,drop 原来的值;
use std::cell::Cell;

pub struct SpiderRobot {
    // ...
    hardware_error_count: Cell<u32>,
    // ...
}

impl SpiderRobot {
    // &self 而非 &mut self
    pub fn add_hardware_error(&self) {
        let n = self.hardware_error_count.get();
        // 先 Copy 旧对象,然后设置为传入的值,然后再 drop 旧对象。
        self.hardware_error_count.set(n + 1);
    }

    pub fn has_hardware_errors(&self) -> bool {
        self.hardware_error_count.get() > 0
    }
}

如果 T 类型没实现 Copy trait,则不能使用 Cell<T>,但可以使用 RefCell<T>:

  1. RefCell::new(value) : 转移 value 的所有权;
  2. ref_cell.borrow():返回对象的共享借用类型 Ref<T>, 如果该对象已经被 &mut 则运行时 panic;
  3. ref_cell.borrow_mut() :返回对象的可变借用类型 RefMut<T> ,如果对象已经被共享借用则运行时 panic;
  4. ref_cell.try_borrow() 和 ref_cell.try_borrow_mut():两个方法都返回一个 Result 供检查,避免 panic;

borrow()/borrow_mut() 返回的 Ref<T>/RefMut<T> 是智能指针,实现了 Deref/DerefMut<Target=T>, 可以直接调用 T 的方法。

对于已经通过 r = RefCell<T>.borrow() 的 r 不能再调用 borrow_mut(), 否则会导致运行时 panic,反之依然。

Rust 对引用和智能指针如 Box<T> 进行 编译时检查 。但是对于 RefCell<T>,Rust 会 在运行时 检查这些规则,在出现违反借用的情况下触发 panic 来提前终止程序。

use std::cell::RefCell;
let ref_cell: RefCell<String> =  RefCell::new("hello".to_string());

let r = ref_cell.borrow();
let count = r.len(); // ok, returns "hello".len()
assert_eq!(count, 5);

let mut w = ref_cell.borrow_mut(); // panic: already borrowed
w.push_str(" world");

// 例 2:
pub struct SpiderRobot {
    // ...
    log_file: RefCell<File>,
    // ...
}

impl SpiderRobot {
    pub fn log(&self, message: &str) {
        let mut file = self.log_file.borrow_mut();
        writeln!(file, "{}", message).unwrap();
    }
}

OnceCell 只能初始化一次,是具有内部可变性的智能指针:

  • 一般使用 get_or_init() 方法来进行初始化;
  • 不能在多线程环境中使用,但可以使用 std::sync::OnceLock
use std::cell::OnceCell;

let cell = OnceCell::new();
// get() 返回 Option,如果未设置则返回 None
assert!(cell.get().is_none());

// cell 虽然没有加 mut,但具有内部可变性
let value: &String = cell.get_or_init(|| {
    "Hello, World!".to_string()
});
assert_eq!(value, "Hello, World!");
assert!(cell.get().is_some());

// set(): 如果已设置,则返回 Err
let cell = OnceCell::new();
assert!(cell.get().is_none());
assert_eq!(cell.set(92), Ok(()));
assert_eq!(cell.set(62), Err(62));
assert!(cell.get().is_some());

LazyCell 和 OnceCell 类似,但是在 new() 函数中传入初始化逻辑,通过实现 Deref<Target=T> 来 第一次解引用时自动调用

  • LayCell 不支持在多线程环境中使用,可以使用 std::sync::LazyLock ;
use std::cell::LazyCell;

let lazy: LazyCell<i32> = LazyCell::new(|| {
    println!("initializing");
    92
});
println!("ready");
println!("{}", *lazy);
println!("{}", *lazy);

Cell 通常和 Rc/Arc<T> 结合使用:Rc/Arc<T> 是引用计数共享,所以 T 只能是 &T 共享引用类型 ,而通过和 Cell 结合使用,可以实现可修改的单例模式:

static GLOBL :Arc<Mutex<i32>> = Arc::new(Mutex::New(1));

use std::cell::{RefCell, RefMut};
use std::collections::HashMap;
use std::rc::Rc;

fn main() {
    let shared_map: Rc<RefCell<_>> = Rc::new(RefCell::new(HashMap::new()));
    // Create a new block to limit the scope of the dynamic borrow
    {
        let mut map: RefMut<'_, _> = shared_map.borrow_mut();
        map.insert("africa", 92388);
        map.insert("kyoto", 11837);
        map.insert("piccadilly", 11826);
        map.insert("marbles", 38);
    }

    // Note that if we had not let the previous borrow of the cache fall out
    // of scope then the subsequent borrow would cause a dynamic thread panic.
    // This is the major hazard of using `RefCell`.
    let total: i32 = shared_map.borrow().values().sum();
    println!("{total}");
}

6 std::collector
#

数组 [N; T] 和容器类型都没有实现 Display trait , 但实现了 Debug trait

Option/Result 是 enum 类型,支持迭代,效果就如一个或 0 个元素。

6.1 Vec
#

Vec 是动态大小, 在堆上分配的连续内存块,可以被 index 操作 &v[a..b] 生成指向对应内存区域 slice(v[a..b] 返回的 slice 是 unsized,故需要加引用)。有三个字段: 长度, 容量和指向堆内存首地址的指针。

Rust 自动扩充 Vec 大小, 这种扩容将老的 Vec 内容 move 到新的连续内存块, 所以:

  1. 有性能开销, 涉及到内存数据的复制移动;
  2. 会导致已有的 Vec Item 的引用失效, 所以在有共享引用的情况下不能修改 Vec;

为了避免容量增长带来的开销, 使用 Vec::with_capacity(n) 来一次性创建容量为 n 的 Vec。

Vec 只能高效的在尾部进行 push/pop 操作, 如果在中间 insert/remove 元素, 则涉及后续元素的移动, 所以 Vec 越长, 中间插入和删除元素性能越差。

Vec 的 index 操作:

  • vec[a..b] :a 和 b 是 usize 类型, 必须小于 vec.len(),否则 panic;
  • vec.get(index):返回一个 Option, 当 index 不存在时返回 None;
// 创建一个空的 vector, len/capacity 均为 0
let mut numbers: Vec<i32> = vec![];

// 用给定的内容创建一个vector, len/capacity 等于元素数目
let words = vec!["step", "on", "no", "pets"];
let mut buffer = vec![0u8; 1024]; // 1024 个 0字节

// 将一个其他集合转换成 vector
let my_vec = my_set.into_iter().collect::<Vec<String>>();

// 获取一个元素的引用
let first_line = &lines[0];
// 获取一个元素的拷贝
let fifth_number = numbers[4];
let second_number = lines[1].clone();
// 获取一个切片的引用
let my_ref = &buffer[4..12];

// 获取一个切片的拷贝:slice.to_vec(), Clone 切片生成 Vec
let my_copy = buffer[4..12].to_vec(); // Vec<T>
let v = [1, 2, 3, 4, 5, 6, 7, 8, 9];
assert_eq!(v.to_vec(), vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
assert_eq!(v[0..6].to_vec(), vec![1, 2, 3, 4, 5, 6]);

// 将 iterable 的所有 item 按顺序添加到 vec 的末尾。它类似于多值版本的 push()。
// iterable 参数可以是任何实现了 IntoIterator<Item=T>。
vec.extend(iterable);

// 类似于 vec.truncate(index),但返回一个 Vec 包含 index 及以后的元素, 原来的
// vec 只包含 index 前的元素,类似于.pop() 的多值版本。
vec.split_off(index);

// 将 vec2 的内容添加到 vec, 然后 vec2 被清空. 这类似于 vec.extend(vec2),
// 除了调用之后 vec2 仍然存在,并且容量不变。
vec.append(&mut vec2);

// 这会从vec中移除范围 vec[range],并返回一个被移除元素的迭代器,其中 range
// 是一个范围值,例如.. 或0..4。
vec.drain(range);

// 移除所有没有通过给定测试的方法。
// 类似于 vec = vec.into_iter().filter(test).collect();
vec.retain(test);

借用 Vec 元素时,不能修改 Vec(修改 Vec 时可能会重新分配内存,从而导致借用失效):

// 不能同时 &mut 和 &Vec 的元素
let mut v = vec![0, 1, 2, 3];
let a = &mut v[i];
let b = &mut v[j]; // error: 不能同时借用 `v` 的多个可变引用。

let first = &v[0];
v.push(6) // erorr:first 共享借用 Vec,不能再修改(需要可变借用)Vec
println!("{}", first);

如果 T 支持 = 和! 运算符,那么数组 [T; N]、切片 [T]、向量 Vec<T> 也支持这些运算符。

如果 T 支持运算符 <、<=、>、>=(PartialOrd trait,见顺序性比较),那么 T 的数组、切片和向量也支持。

由于 Vec<T> 可以被 Deref<Targe=[T]>, 所以 Vec 对象也继承了 slice [T] 的方法:

Vec 方法:

// 创建
pub const fn new() -> Vec<T>
    let mut vec: Vec<i32> = Vec::new();

pub fn with_capacity(capacity: usize) -> Vec<T>
    let mut vec = Vec::with_capacity(10);
    // The vector contains no items, even though it has capacity for more
    assert_eq!(vec.len(), 0);
    assert!(vec.capacity() >= 10);

pub unsafe fn from_raw_parts( ptr: *mut T, length: usize, capacity: usize ) -> Vec<T>

pub const fn new_in(alloc: A) -> Vec<T, A>
pub fn with_capacity_in(capacity: usize, alloc: A) -> Vec<T, A>
pub unsafe fn from_raw_parts_in( ptr: *mut T, length: usize, capacity: usize, alloc: A ) -> Vec<T, A>
pub fn into_raw_parts(self) -> (*mut T, usize, usize)
pub fn into_raw_parts_with_alloc(self) -> (*mut T, usize, usize, A)

pub fn capacity(&self) -> usize
// 确保 capacity() >= len() + additional
pub fn reserve(&mut self, additional: usize)
    let mut vec = vec![1];
    vec.reserve(10);
    assert!(vec.capacity() >= 11);

pub fn reserve_exact(&mut self, additional: usize)
pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError>
pub fn try_reserve_exact( &mut self, additional: usize ) -> Result<(), TryReserveError>

pub fn shrink_to_fit(&mut self)
    let mut vec = Vec::with_capacity(10);
    vec.extend([1, 2, 3]);
    assert!(vec.capacity() >= 10);
    vec.shrink_to_fit();
    assert!(vec.capacity() >= 3);

// 将 vector 的 capacity 收缩到指定的最小值
pub fn shrink_to(&mut self, min_capacity: usize)
    let mut vec = Vec::with_capacity(10);
    vec.extend([1, 2, 3]);
    assert!(vec.capacity() >= 10);
    vec.shrink_to(4);
    assert!(vec.capacity() >= 4);
    vec.shrink_to(0);
    assert!(vec.capacity() >= 3);

// 将 vec 转换为 Box<[T]> , 该方法会丢弃 capacity 超过 shink_to_fit 的空间
pub fn into_boxed_slice(self) -> Box<[T], A>
    let mut vec = Vec::with_capacity(10);
    vec.extend([1, 2, 3]);
    assert!(vec.capacity() >= 10);
    let slice = vec.into_boxed_slice();
    assert_eq!(slice.into_vec().capacity(), 3);

pub fn truncate(&mut self, len: usize)
    let mut vec = vec![1, 2, 3];
    vec.truncate(8);
    assert_eq!(vec, [1, 2, 3]);

// 等效于 &s[..]
pub fn as_slice(&self) -> &[T]
    use std::io::{self, Write};
    let buffer = vec![1, 2, 3, 5, 8];
    io::sink().write(buffer.as_slice()).unwrap();

pub fn as_mut_slice(&mut self) -> &mut [T]
pub fn as_ptr(&self) -> *const T
pub fn as_mut_ptr(&mut self) -> *mut T

// 返回底层使用的内存分配器
pub fn allocator(&self) -> &A

// 将长度设置为 new_len,一般使用安全方法 truncate, resize, extend, or clear.
// new_len 需要小于等于 capacity(), 而且 old_len..new_len must be initialized.
pub unsafe fn set_len(&mut self, new_len: usize)

// 使用 vec 最后一个元素替换 index 元素,返回 index 元素。
// 一般用于修改没有实现 Copy 的 Vec(不能 v[index] = value, 因为 v[index] 会部分
// 转移元素,这是不允许的)
pub fn swap_remove(&mut self, index: usize) -> T
    let mut v = vec!["foo", "bar", "baz", "qux"];
    assert_eq!(v.swap_remove(1), "bar");
    assert_eq!(v, ["foo", "qux", "baz"]);
    assert_eq!(v.swap_remove(0), "foo");
    assert_eq!(v, ["baz", "qux"]);

// 插入和删除元素,会导致后续元素批量移动。
pub fn insert(&mut self, index: usize, element: T)
pub fn remove(&mut self, index: usize) -> T

// 只保留 f 返回 true 的元素
pub fn retain<F>(&mut self, f: F) where F: FnMut(&T) -> bool
    let mut vec = vec![1, 2, 3, 4];
    vec.retain(|&x| x % 2 == 0);
    assert_eq!(vec, [2, 4]);

pub fn retain_mut<F>(&mut self, f: F) where F: FnMut(&mut T) -> bool

// 使用指定 F 来过滤重复的元素,如果连续元素的 F 调用返回值相同则被过滤
pub fn dedup_by_key<F, K>(&mut self, key: F) where F: FnMut(&mut T) -> K, K: PartialEq
    let mut vec = vec![10, 20, 21, 30, 20];
    vec.dedup_by_key(|i| *i / 10);
    assert_eq!(vec, [10, 20, 30, 20]);

pub fn dedup_by<F>(&mut self, same_bucket: F) where F: FnMut(&mut T, &mut T) -> bool
    let mut vec = vec!["foo", "bar", "Bar", "baz", "bar"];
    vec.dedup_by(|a, b| a.eq_ignore_ascii_case(b));
    assert_eq!(vec, ["foo", "bar", "baz", "bar"]);

pub fn push(&mut self, value: T)
pub fn push_within_capacity(&mut self, value: T) -> Result<(), T>
pub fn pop(&mut self) -> Option<T>

// 将 other 的元素移动到 self,other 被清空(other 没有被 drop,容量不变):
pub fn append(&mut self, other: &mut Vec<T, A>)
    let mut vec = vec![1, 2, 3];
    let mut vec2 = vec![4, 5, 6];
    vec.append(&mut vec2);
    assert_eq!(vec, [1, 2, 3, 4, 5, 6]);
    assert_eq!(vec2, []);

// 从 self 删除 range 的元素,返回 range 元素的迭代器
pub fn drain<R>(&mut self, range: R) -> Drain<'_, T, A> where R: RangeBounds<usize>
    let mut v = vec![1, 2, 3];
    let u: Vec<_> = v.drain(1..).collect();
    assert_eq!(v, &[1]);
    assert_eq!(u, &[2, 3]);
    // A full range clears the vector, like `clear()` does
    v.drain(..);
    assert_eq!(v, &[]);

pub fn clear(&mut self)
pub fn len(&self) -> usize
pub fn is_empty(&self) -> bool

// 将 self 从 at 位置切分为两个,self 为 at 前元素,返回 at 及以后的元素
pub fn split_off(&mut self, at: usize) -> Vec<T, A> where A: Clone
    let mut vec = vec![1, 2, 3];
    let vec2 = vec.split_off(1);
    assert_eq!(vec, [1]);
    assert_eq!(vec2, [2, 3]);

// 调整 vec,使其长度达到 new_len, 新的元素使用 f 返回
pub fn resize_with<F>(&mut self, new_len: usize, f: F) where F: FnMut() -> T
    let mut vec = vec![1, 2, 3];
    vec.resize_with(5, Default::default);
    assert_eq!(vec, [1, 2, 3, 0, 0]);
    let mut vec = vec![];
    let mut p = 1;
    vec.resize_with(4, || { p *= 2; p });
    assert_eq!(vec, [2, 4, 8, 16]);

// 返回一个具有指定 lifetime 的 slice
pub fn leak<'a>(self) -> &'a mut [T] where A: 'a

pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit<T>]
pub fn split_at_spare_mut(&mut self) -> (&mut [T], &mut [MaybeUninit<T>])

pub fn resize(&mut self, new_len: usize, value: T)
    let mut vec = vec!["hello"];
    vec.resize(3, "world");
    assert_eq!(vec, ["hello", "world", "world"]);
    let mut vec = vec![1, 2, 3, 4];
    vec.resize(2, 0);
    assert_eq!(vec, [1, 2]);

pub fn extend_from_slice(&mut self, other: &[T])
    let mut vec = vec![1];
    vec.extend_from_slice(&[2, 3, 4]);
    assert_eq!(vec, [1, 2, 3, 4]);

pub fn extend_from_within<R>(&mut self, src: R) where R: RangeBounds<usize>

// 将 range 元素用 replace_with 替换, 返回替换前的 range 元素
pub fn splice<R, I>( &mut self, range: R, replace_with: I) -> Splice<'_, <I as IntoIterator>::IntoIter, A> where R: RangeBounds<usize>, I: IntoIterator<Item = T>
    let mut v = vec![1, 2, 3, 4];
    let new = [7, 8, 9];
    let u: Vec<_> = v.splice(1..3, new).collect();
    assert_eq!(v, &[1, 7, 8, 9, 4]);
    assert_eq!(u, &[2, 3]);

pub fn extract_if<F>(&mut self, filter: F) -> ExtractIf<'_, T, F, A> where F: FnMut(&mut T) -> bool
    #![feature(extract_if)]
    let mut numbers = vec![1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15];
    let evens = numbers.extract_if(|x| *x % 2 == 0).collect::<Vec<_>>();
    let odds = numbers;
    assert_eq!(evens, vec![2, 4, 6, 8, 14]);
    assert_eq!(odds, vec![1, 3, 5, 9, 11, 13, 15]);

6.2 VecDeque
#

VecDeque 是动态大小, 在堆上分配的环形缓冲区, 有 start 和 end 指针。

VecDeque主要是能快速在开头和尾部 push/pop 元素。和 Vec 不同的是, 数据并不一定从内存区域的开始存储, 也可以在尾部自动回环, 所以内存不一定连续,故它没有实现 Deref<Target=[T]> ,不能通过 slice 操作来创建 slice,和调用 slice 的方法。

VecDeque 支持 index 操作, 如 deque[index],但是不支持 range slice。

pub fn front(&self) -> Option<&T>
pub fn front_mut(&mut self) -> Option<&mut T>
pub fn back(&self) -> Option<&T>
pub fn back_mut(&mut self) -> Option<&mut T>
pub fn pop_front(&mut self) -> Option<T>
pub fn pop_back(&mut self) -> Option<T>
pub fn push_front(&mut self, value: T)
pub fn push_back(&mut self, value: T)
pub fn binary_search(&self, x: &T) -> Result<usize, usize>

6.3 LinkedList
#

LinkedList 是双端链接的 List,它允许从两端 push 和 pop 元素。

use std::collections::LinkedList;

let list = LinkedList::from([1, 2, 3]);

部分方法:

pub const fn new() -> LinkedList<T>
pub fn append(&mut self, other: &mut LinkedList<T>)

pub fn front(&self) -> Option<&T>
pub fn front_mut(&mut self) -> Option<&mut T>
pub fn back(&self) -> Option<&T>
pub fn back_mut(&mut self) -> Option<&mut T>
pub fn push_front(&mut self, elt: T)
pub fn pop_front(&mut self) -> Option<T>
pub fn push_back(&mut self, elt: T)
pub fn pop_back(&mut self) -> Option<T>

6.4 BinaryHeap
#

BinaryHeap<T> 集合始终以某种形式组织元素,最大元素总是会被移动到队列的首部。

BinaryHeap 并不仅限于数字,它可以包含任何实现了 Ord trait 的类型。

BinaryHeap 可以用作一个工作队列:定义一个任务结构体,然后根据任务的优先级实现 Ord,让高优先级的任务大于低优先级的任务。然后创建一个 BinaryHeap 来保存所有待办的任务。它的 .pop() 方法将总是返回最重要的任务。

heap.push(value); // 向堆中添加一个元素
heap.pop(); // 移除并返回堆中最大的值。返回 Option<T>,如果堆为空时返回 None。
heap.peek(); // 返回堆中最大的值的引用。返回类型是 Option<&T>。

if let Some(top) = heap.peek_mut() {
    if *top > 10 {
        PeekMut::pop(top);
    }
}

use std::collections::BinaryHeap;

let mut heap = BinaryHeap::from(vec![2, 3, 8, 6, 9, 5, 4]);
// 值 9 在堆的顶部:
assert_eq!(heap.peek(), Some(&9));
assert_eq!(heap.pop(), Some(9));
// 移除 9 也会重新排布其他元素,把 8 移动到头部,等等:
assert_eq!(heap.pop(), Some(8));
assert_eq!(heap.pop(), Some(6));
assert_eq!(heap.pop(), Some(5));

BinaryHeap 是可迭代的对象,并且它有 .iter() 方法,但这个迭代器 以任意顺序 产生堆中的元素,而不是按照从大到小的顺序。

为了按照大小顺序消耗 BinaryHeap 中的值,可以使用 while-pop 循环:

while let Some(task) = heap.pop() {
    handle(task);
}

6.5 HashMap/BTreeMap
#

HashMap 是键值对(称为条目 entry)的集合,条目的键都不同,方便高效查找。

两种 map 类型: HashMap<K, V> 和 BTreeMap<K, V> ,共享了很多相同的方法, 不同之处在于它们组织条目的方式。

  1. HashMap: 把键和值都存储在哈希表中,因此它要求键的类型 K 实现了 Hash 和 Eq :

    • bool、int、uint、String、&str 等;
    • float 没有实现这两个 trait,不能用于 key;
    • 对于 collection,如果它们的元素类型都实现了 Hash 和 Eq,则 collection 也实现了 Hash 和 Eq,例如 Vec<u8>;
  2. BTreeMap: 按照键的顺序在 树形结构中 存储条目,键的类型 K 需要实现 Ord。

总结:

  1. HashMap 是使用 一块连续的 heap 来保存 map 元素的, 如果 map 容量要增加, 则需要分配一块新的连续内存区域, 并将老的 map 元素移动过去,有一定性能开销;
  2. BTreeMap 是使用 Node 来保存元素, 不是连续内存区域, 便于随机插入和读取 ;
// 从键值对创建并填充新的 HashMap 或 BTreeMap。
// iter 必须是一个 Iterator<Item=(K, V)>。
iter.collect();

// 如果 map 里有给定 key 的条目则返回 true。
map.contains_key(&key);

// 在 map 中查找给定 key 的条目。如果找到了匹配的条目,就返回 Some(r),
// 其中 r 是相应的值的引用。否则返回 None。
map.get(&key);

在查询 map 时,传入的 key 类型 B 和 map 定义的 key 类型 K 可以不一致,但需要满足 B = Borrow<K>;

map 实现了 Index trait,故支持 map[&key] 操作,然而如果没有给定的 key 的条目存在, 则会 panic ,就类似越界访问数组一样。

不支持 map[&key] = value 赋值 ,但可以使用 map.insert(key, value) 来插入和返回值(旧值)。

因为 BTreeMap<K, V> 按照键的顺序保存条目,所以它支持一个附加的操作 btree_map.split_off(&key):把 btree_map 分割成两个。键小于 key 的条目被留在 btree_map 中,返回一个包含其余条目的新BTreeMap<K, V>。

map 支持 Entry 操作:

let record = student_map.entry(name.to_string()).or_insert_with(Student::new);

student_map.entry(name.to_string()) 返回的 Entry 值类似于 可变引用 ,它指向 map 中一个已经被键值对占据 (occupied) 的位置或者是空的 (vacant) 的位置。如果为空,条目的 .or_insert_with() 方法会插入一个新的 Student。

  • Entry 以 &mut 获得 map 的可变引用;
  • entry(key) 的 key 类型必须和 map key 类型 一致 。(不支持 k = Borrowed<Q>)
// 对给定的 key 返回一个 Entry。如果 map 中没有这个 key,它会返回一个空的 Entry。
// 这个方法以 &mut 引用获取 self 参数,并返回一个生命周期相同的 Entry:
pub fn entry<'a>(&'a mut self, key: K) -> Entry<'a, K, V>

// Entry 类型有一个生命周期参数'a,因为它是 map 的一种 mut 借用。只要 Entry 存在,
// 它就有 map 的独占访问权限。
// entry() 方法的 key 是 K 类型,而不是 Borrow<Q> 类型,所以如果 map key 是
// String 类型
// 就不能传入 &str.
map.entry(key);

// 确保 map 包含给定的 key 的条目,如果需要的话用给定的 value 插入一个新的条目。
// 它返回新插入的或者现有的值的 mut 引用。
map.entry(key).or_insert(value);

let mut vote_counts: HashMap<String, usize> = HashMap::new();
for name in ballots {
    // .or_insert() 返回一个可变引用,因此 count 的类型是 &mut usize。
    let count = vote_counts.entry(name).or_insert(0);
    *count += 1;
}

// 确保 map 包含给定的 key 的条目,如果需要的话用 Default::default() 返回的值插入
// 一个新条目
map.entry(key).or_default();

// 除了当它需要创建新的条目时,它会调用 default_fn() 来产生默认值。
map.entry(key).or_insert_with(default_fn);

// 这个 map 中包含每个单词和出现它的文件的集合。
let mut word_occurrence: HashMap<String, HashSet<String>> = HashMap::new();
for file in files {
    for word in read_words(file)? {
        let set = word_occurrence
            .entry(word)
            .or_insert_with(HashSet::new);
        set.insert(file.clone());
    }
}

// 如果给定的 key 的条目存在就调用 closure,值的可变引用传进闭包, 返回 Entry,
// 因此它可以和其它方法链式调用。
map.entry(key).and_modify(closure);

// 这个 map 包含给定字符串中的所有单词,以及它们出现的次数。
let mut word_frequency: HashMap<&str, u32> = HashMap::new();
for c in text.split_whitespace() {
    word_frequency.entry(c)
        .and_modify(|count| *count += 1)
        .or_insert(1);
}

迭代 map:

  1. 以值迭代:for (k, v) in map,产生(K, V)对, 这会消耗掉 map。
  2. 迭代共享引用:for (k, v) in &map,产生(&K, &V)对。
  3. 迭代可变引用:for (k, v) in &mut map,产生(&K, &mut V)对。
  4. map.keys(): 返回一个只迭代键的迭代器,以引用的形式返回。
  5. map.values():返回一个只迭代值的迭代器,以引用的形式返回。
  6. map.values_mut():返回一个只迭代值的迭代器,以 mut 引用的形式返回。

迭代 map 返回的 key 都是共享引用,不能对 key 进行修改。

HashMap 迭代器都会以 任意顺序 访问条目。BTreeMap 的迭代器按照 键顺序 访问。

6.6 HashSet/BTreeSet
#

set 是只有键的 map,事实上 Rust 的 HashSet<T>和 BTreeSet<T> 被实现为 HashMap<T, ()> 和 BTreeMap<T, ()> 的浅包装。

HashMap 和 HashSet 的 key 必须要实现 Hash 和 Eq。

两种迭代 set 的方法:

  1. 以值迭代(for v in set)产生set的成员(并消耗这个 set)。
  2. 以共享引用迭代(for v in &set)产生set中成员的共享引用。

不支持以 mut 引用迭代 set。也没有方法获取 set 中值的 mut 引用。

set.iter() 返回一个以共享引用方式迭代 set 的迭代器。

HashSet 迭代器类似于 HashMap 的迭代器,也会以任意顺序产生值。

BTreeSet 迭代器按照顺序产生值,类似于一个排序过的 Vec。

set 的一些方法:

  1. set.get(&value): 返回 value 的共享引用,类型是 Option<&T>;
  2. set.take(&value): 与 set.remove(&value) 类似,返回移除的值,类型是 Option<&T>;
  3. set.replace(value);

两个 set 间的方法:

  1. set1.intersection(&set2) : 返回同时出现在 set1 和 set2 中的值的迭代器;
  2. &set1 & &set2: 返回一个新 set,是 set1 和 set2 的交集;
  3. set1.union(&set2): 返回并集迭代器;等效于&set1 | &set2
  4. set1.difference(&set2): 返回差集迭代器; 等效于 &set1 - &set2
  5. set1.symmetric_difference(&set2): 返回对称差(异或)迭代器,等效于: &set1 ^ &set2;

6.7 Hash
#

std::hash::Hash 是标准库用于可哈希类型的 trait。HashMap 的键和 HashSet 的元素必须实现 Hash 和 Eq。

大多数实现了 Eq 的内建类型也都实现了 Hash。

整数类型、char、String 都是可哈希类型;

当元素是可哈希类型时,元组、数组、切片、vector 也是可哈希类型。

标准库的一个原则是不管你把一个值存储到哪里或者如何指向它,它必须有相同的哈希值。因此,引用和被引用的值有相同的哈希值。Box 和被装箱的值有相同的哈希值。一个 vector vec 和包含它的所有元素的切片 &vec[..] 有相同的哈希值。一个 String 和一个有相 同字符的 &str 有相同的哈希值。

结构和枚举默认没有实现 Hash,但可以派生实现(前提是所有字段实现了 Hash):

#[derive(Clone, PartialEq, Eq, Hash)]
enum MuseumNumber {
    // ...
}

7 std::convert
#

Enum std::convert::Infallible 用于 Result 中的 error type, 表示不可能发生的错误 (也就是 Result 只能一直是 Ok), 这是通过定义没有 variant 的 enum Infallible 类型来实现的:

pub enum Infallible {}
impl<T, U> TryFrom<U> for T where U: Into<T> {
    type Error = Infallible;

    fn try_from(value: U) -> Result<Self, Infallible> {
        Ok(U::into(value))  // 从不会返回 Err
    }
}

8 std::panic
#

panic 是最简单的异常处理机制,它先打印 error message,然后开始 unwinding stack,最后退出当前 thread:

  1. 如果是 main thread panic,则程序退出;
  2. 否则,如果是 spawned thread panic,则该 thread 会终止,程序不退出;

unwinding stack 过程中,Rust 会回溯调用栈,drop 调用 stack 涉及的所有对象和资源。

使用 RUST_BACKTRACE=1 cargo run 来打印 stack 详情:

fn drink(beverage: &str) {
    // You shouldn't drink too much sugary beverages.
    if beverage == "lemonade" { panic!("AAAaaaaa!!!!"); }

    println!("Some refreshing {} is all I need.", beverage);
}

fn main() {
    drink("water");
    drink("lemonade");
    drink("still water");
}

如果 panic 是 FFI 调用的外部库函数导致的,则 Rust 不会进行 unwinding,而是直接 panic。

也可以在 Cargo.toml 里设置 panic 时不 unwiding stack 而是直接 abort 退出(如果设置了 panic hook,则 abort 退出前会先调用该 hook):

[profile.release]
panic = 'abort'

std::panic module 提供的函数:

  • catch_unwind : Invokes a closure, capturing the cause of an unwinding panic if one occurs.
  • panic_any : Panic the current thread with the given message as the panic payload.
  • resume_unwind : Triggers a panic without invoking the panic hook.
  • set_hook : Registers a custom panic hook, replacing the previously registered hook.
  • take_hook : Unregisters the current panic hook and returns it, registering the default hook in its place.
  • always_abort : Make all future panics abort directly without running the panic hook or unwinding.
  • get_backtrace_style : Checks whether the standard library’s panic hook will capture and print a backtrace.
  • set_backtrace_style : Configure whether the default panic hook will capture and display a backtrace.
  • update_hook : Atomic combination of take_hook and set_hook. Use this to replace the panic handler with a new panic handler that does something and then executes the old handler.

std::panic::cach_unwind() 捕获闭包中的 panic 并返回一个 Result:

  • 当闭包内 panic 时,返回 Err, 否则返回 Ok;
  • 如果 panic 使用 unwinding 实现,则会捕获。否则如果是 abort 实现,则不会捕获;
  • 如果设置了 panic hook, 则该 hook 在 panic 被捕获前被 unwinding 前调用;
  • Also note that unwinding into Rust code with a foreign exception (e.g. an exception thrown from C++ code) is undefined behavior.
pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R>

use std::panic;

let result = panic::catch_unwind(|| {
    println!("hello!");
});
assert!(result.is_ok());

let result = panic::catch_unwind(|| {
    panic!("oh no!");
});
assert!(result.is_err());

std::panic::resume_unwind() 来触发一个 不执行 panic hook 的 panic:

  • 如果 panic 被配置使用 abort 实现,则该函数会 abort 程序而不是 unwinding;
pub fn resume_unwind(payload: Box<dyn Any + Send>) -> !

use std::panic;

let result = panic::catch_unwind(|| {
    panic!("oh no!");
});

if let Err(err) = result {
    panic::resume_unwind(err);
}

std::panic::panic_any(msg: M) 来触发 执行 panic hook 的 panic ,其中 M 可以是任意类型。但 panic!() 的参数只能是和 println!() 类似的格式化字符串。

// msg 可以任意类型(Any + Send)而不只是 string。
// 后续可以使用 PanicInfo::payload() 来获取 mesage。
pub fn panic_any<M: 'static + Any + Send>(msg: M) -> !

panic!();
panic!("this is a terrible mistake!");
panic!("this is a {} {message}", "fancy", message = "message");
// panic with the value of 4 to be collected elsewhere
std::panic::panic_any(4);

std::panic::set_hook() 设置自定义 panic hook(只能生效一个,取代以前的 hook)

  • 在 thread panic 时,但 panic runtime 调用前执行,所以对于 aborting 和 unwinding 都适用
  • 缺省的 hook 是程序启动时自动注册的,会在 stderr 打印 message,并按需打印 backtrace;
pub fn set_hook(hook: Box<dyn Fn(&PanicInfo<'_>) + Sync + Send + 'static>)

// 示例
use std::panic;

// panic 时传入一个 PanicInfo 对象,可以用来提取 panic 信息
panic::set_hook(Box::new(|_| {
    println!("Custom panic hook");
}));

panic!("Normal panic");

std::panic::take_hook() 将 panic hook 设置为缺省 hook:

pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + Sync + Send + 'static>

use std::panic;

panic::set_hook(Box::new(|_| {
    println!("Custom panic hook");
}));

let _ = panic::take_hook();

panic!("Normal panic");

std::panic::update_hook() 是 take_hook/set_hook 的原子操作,用来替换当前 hook 并返回以前的 hook:

#![feature(panic_update_hook)]
use std::panic;

// Equivalent to
// let prev = panic::take_hook();
// panic::set_hook(move |info| {
//     println!("...");
//     prev(info);
// );
panic::update_hook(move |prev, info| {
    println!("Print custom message and execute panic handler as usual");
    prev(info);
});

panic!("Custom and then normal");

set_hook/get_hook 使用的 std::panic::PanicInfo 对象提供了 location()/message()/payload() 方法,提供 panic 信息:

use std::panic;

panic::set_hook(Box::new(|panic_info| {
    if let Some(location) = panic_info.location() {
        println!("panic occurred in file '{}' at line {}",
            location.file(),
            location.line(),
        );
    } else {
        println!("panic occurred but can't get location information...");
    }
}));

panic!("Normal panic");

std::panic::set_backtrace_style/get_backtrace_style() 用于设置和返回 panic 时 panic hook 是否能捕获和显示 backtrace:

  • 缺省值可以使用环境变量 RUST_BACKTRACE 来配置。
  • for BacktraceStyle::Off
  • for BacktraceStyle::Full
  • for BacktraceStyle::Short

Other values are currently BacktraceStyle::Short, but this may change in the future:

#[non_exhaustive]
pub enum BacktraceStyle {
    Short,
    Full,
    Off,
}

get_backtrace_style() 先查找 set_backtrace_style() 设置的值,如果没有设置则查找 RUST_BACKTRACE 环境变量。

pub fn set_backtrace_style(style: BacktraceStyle)
pub fn get_backtrace_style() -> Option<BacktraceStyle>

9 std::error
#

Rust 提供了两种错误处理类型:

  1. panic runtime 和接口;
  2. Result, std::error::Error trait 和用户自定义错误类型;

通过使用如下两个方法,可以将 Error 转换为 panic:

  • Result::unwrap()
  • Result::expect(&str)

Trait std::error::Error:

  • Error trait 没有必须方法;
  • Error trait 是 Debug + Display 的子 trait,所以实现 Error trait 的类型必须同时实现 Display 和 Debug trait。
  • 报错信息一般是小写字符串;
pub trait Error: Debug + Display {
    // 返回引发该 Error 的深层次错误,该错误类型必须也实现了 Error trait。
    fn source(&self) -> Option<&(dyn Error + 'static)> { ... }

    // description 和 cause 方法已被弃用
    fn description(&self) -> &str { ... } // 用 to_string() 代替
    fn cause(&self) -> Option<&dyn Error> { ... } // 用 source() 代替

    // 目前仅标准库内部使用(std::error::Report 的 show_backtrace() 方法需要)
    fn provide<'a>(&'a self, request: &mut Request<'a>) { ... }
}

let err = "NaN".parse::<u32>().unwrap_err();
assert_eq!(err.to_string(), "invalid digit found in string");

自定义实现 Error trait 的类型:

  1. 必须实现 Display 和 Debug trait;
  2. 可以重新定义 Error::source() 方法,后续调用方获得下一层(根因)错误:
use std::error::Error;
use std::fmt;

// 自定义错误类型必须同时实现 Debug 和 Display trait
#[derive(Debug)]
struct SuperError {
    source: SuperErrorSideKick,
}

impl fmt::Display for SuperError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "SuperError is here!")
    }
}

impl Error for SuperError {
    // SuperError 重新定义了 source 方法,返回内部(更底层)的错误的借用
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        Some(&self.source)
    }
}

#[derive(Debug)]
struct SuperErrorSideKick;

impl fmt::Display for SuperErrorSideKick {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "SuperErrorSideKick is here!")
    }
}

// Error 都是可选方法或函数,所以可以不实现。
impl Error for SuperErrorSideKick {}

fn get_super_error() -> Result<(), SuperError> {
    Err(SuperError { source: SuperErrorSideKick })
}

fn main() {
    match get_super_error() {
        Err(e) => {
            println!("Error: {e}");
            // 手动调用 source() 方法来返回内部根因
            println!("Caused by: {}", e.source().unwrap());
        }
        _ => println!("No error"),
    }
}

标准库为实现 Error 的 trait object,提供了 is/downcase_xx<T>)() 方法,用于对 Error 类型进行 下钻处理

// 下面 3 种类型均提供了如下 4 个方法。
impl dyn Error
impl dyn Error + Send
impl dyn Error + Send + Sync

// 以 impl dyn Error 为例
pub fn is<T>(&self) -> bool where T: Error + 'static
pub fn downcast_ref<T>(&self) -> Option<&T> where T: Error + 'static
pub fn downcast_mut<T>(&mut self) -> Option<&mut T> where T: Error + 'static
pub fn downcast<T>( self: Box<dyn Error> ) -> Result<Box<T>, Box<dyn Error>> where T: Error + 'static

也可以使用 std::error module 提供的两个函数来返回 trait object 的类型引用或值:

request_ref
Request a reference of type T from the given impl Error.
request_value
Request a value of type T from the given impl Error.
// 从 trait object 返回引用
pub fn request_ref<T, 'a>(err: &'a (impl Error + ?Sized)) -> Option<&'a T> where T: 'static + ?Sized,
use core::error::Error;
use core::error::request_ref;

fn get_str(err: &impl Error) -> &str {
    request_ref::<str>(err).unwrap()
}

// 从 trait object 返回 value
pub fn request_value<T, 'a>(err: &'a (impl Error + ?Sized)) -> Option<T> where T: 'static,
use std::error::Error;
use core::error::request_value;
fn get_string(err: &impl Error) -> String {
    request_value::<String>(err).unwrap()
}

Struct std::error::Report<<E=Box<dyn Error>> 用来打印 E 的信息:

pub fn new(error: E) -> Report<E>
pub fn pretty(self, pretty: bool) -> Self
pub fn show_backtrace(self, show_backtrace: bool) -> Self

// 示例
#![feature(error_reporter)]
use std::error::Report;

fn main() -> Result<(), Report<SuperError>> {
    get_super_error()
        .map_err(Report::from)
        .map_err(|r| r.pretty(true).show_backtrace(true))?;
    Ok(())
}

// 输出
// Error: SuperError is here!

// Caused by:
//       SuperErrorSideKick is here!


#![feature(error_reporter)]
use std::error::Report;

let source = SuperErrorSideKickSideKick;
let source = SuperErrorSideKick { source };
let error = SuperError { source };
let report = Report::new(error).pretty(true);
eprintln!("Error: {report:?}");

// 输出
// Error: SuperError is here!

// Caused by:
//    0: SuperErrorSideKick is here!
//    1: SuperErrorSideKickSideKick is here!

std::error::Report 的 show_backtrace() 方法使用 Error 实现的 provide() 方法来打印 backtrace:

#![feature(error_reporter)]
#![feature(error_generic_member_access)]
use std::error::Request;
use std::error::Report;
use std::backtrace::Backtrace;

#[derive(Debug)]
struct SuperErrorSideKick {
    backtrace: Backtrace,
}

impl SuperErrorSideKick {
    fn new() -> SuperErrorSideKick {
        SuperErrorSideKick { backtrace: Backtrace::force_capture() } // 捕获 backtrace
    }
}

impl Error for SuperErrorSideKick {
    fn provide<'a>(&'a self, request: &mut Request<'a>) {
        request.provide_ref::<Backtrace>(&self.backtrace); // 提供 Backtrace 类型
    }
}

let source = SuperErrorSideKick::new();
let error = SuperError { source };
// show_backtrace 依赖于 Error 实现 provide() 方法。
let report = Report::new(error).pretty(true).show_backtrace(true);
eprintln!("Error: {report:?}");

// 输出:
// Error: SuperError is here!

// Caused by:
//       SuperErrorSideKick is here!

// Stack backtrace:
//    0: rust_out::main::_doctest_main_src_error_rs_1158_0::SuperErrorSideKick::new
//    1: rust_out::main::_doctest_main_src_error_rs_1158_0
//    2: rust_out::main
//    3: core::ops::function::FnOnce::call_once
//    4: std::sys_common::backtrace::__rust_begin_short_backtrace
//    5: std::rt::lang_start::{{closure}}
//    6: std::panicking::try
//    7: std::rt::lang_start_internal
//    8: std::rt::lang_start
//    9: main
//   10: __libc_start_main
//   11: _start

标准库提供了 &str/String/Cow<’_, str> 到 Box<dyn Error> 的 From 转换:

impl<'a> From<&str> for Box<dyn Error + 'a>
impl<'a> From<&str> for Box<dyn Error + Sync + Send + 'a>
impl<'a, 'b> From<Cow<'b, str>> for Box<dyn Error + 'a>
impl<'a, 'b> From<Cow<'b, str>> for Box<dyn Error + Sync + Send + 'a>
impl<'a, E> From<E> for Box<dyn Error + 'a> where E: Error + 'a

impl<'a, E> From<E> for Box<dyn Error + Sync + Send + 'a>
  where E: Error + Send + Sync + 'a

impl<'a> From<String> for Box<dyn Error + 'a>
impl<'a> From<String> for Box<dyn Error + Sync + Send + 'a>

10 std::hash
#

std::hash module 提供了对 Rust 类型计算 hash 值的支持。hash 值在 HashMap 和 HashSet 中被广泛使用。

Rust 类型需要先实现 std::hash::Hash trait, 然后才能使用 std::hash::Hasher 来计算 hash 值。

  • 对于任意实现了 Hash 和 Eq 类型, k1 == k2 -》hash(k1) == hash(k2) , HashMap 和 HashSet 都依赖于这个语义;通过 #[derive(PartialEq, Eq, Hash)] 来确保。
  • Rust 为绝大部分标准类型实现了 Hash trait, 如 str/String/Path/PathBuf/Cow 等。
pub trait Hash {
    // Required method
    fn hash<H>(&self, state: &mut H) where H: Hasher;

    // Provided method
    fn hash_slice<H>(data: &[Self], state: &mut H) where H: Hasher, Self: Sized
}

// Hash 一个基本类型
use std::hash::{DefaultHasher, Hash, Hasher};
let mut hasher = DefaultHasher::new();
7920.hash(&mut hasher);
println!("Hash is {:x}!", hasher.finish());

// Hash 一个 slice
let numbers = [6, 28, 496, 8128];
Hash::hash_slice(&numbers, &mut hasher);
println!("Hash is {:x}!", hasher.finish());

最简单的实现 Hash trait 的方式是使用 #[derive(Hash)]

use std::hash::{DefaultHasher, Hash, Hasher};

#[derive(Hash)]
struct Person {
    id: u32,
    name: String,
    phone: u64,
}
let person1 = Person {
    id: 5,
    name: "Janet".to_string(),
    phone: 555_666_7777,
};
let person2 = Person {
    id: 5,
    name: "Bob".to_string(),
    phone: 555_666_7777,
};

assert!(calculate_hash(&person1) != calculate_hash(&person2));

fn calculate_hash<T: Hash>(t: &T) -> u64 {
    let mut s = DefaultHasher::new();
    t.hash(&mut s); // t 实现了 Hash trait,故具有 hash() 方法,需要传入一个 Hasher
    s.finish() // 返回计算的 hash 值,是 u64 类型。
}

也可以手动实现 Hash trait:

use std::hash::{DefaultHasher, Hash, Hasher};

struct Person {
    id: u32,
    name: String,
    phone: u64,
}

impl Hash for Person {
    fn hash<H: Hasher>(&self, state: &mut H) {
// Rust 已经为基本类型实现了 Hash trait,故可以挨个调用各 field 来计算 hash 值。
// 注意:
//  1. hash() 方法没有返回值;传入的 state 对应的 Hasher 内部维护有状态;
//  2. 没有调用 state finish() 方法,而在后续对 Person 计算整体 hash 值时才调用该方法。
        self.id.hash(state);
        self.phone.hash(state);
    }
}

let person1 = Person {
    id: 5,
    name: "Janet".to_string(),
    phone: 555_666_7777,
};
let person2 = Person {
    id: 5,
    name: "Bob".to_string(),
    phone: 555_666_7777,
};

assert_eq!(calculate_hash(&person1), calculate_hash(&person2));

fn calculate_hash<T: Hash>(t: &T) -> u64 {
    let mut s = DefaultHasher::new();
    t.hash(&mut s);
    s.finish()
}

Trait std::hash::Hasher 定义了一个 Hash 算法类型需要实现的接口:

  • Hasher 内部包含状态 ,可以多次调用 Hasher 的 write/write_xx() 方法,最终调用 finish() 返回 hash 值;
pub trait Hasher {
    // Required methods
    fn finish(&self) -> u64;
    fn write(&mut self, bytes: &[u8]); // 计算任意字节序列的 hash 值。

    // Provided methods
    fn write_u8(&mut self, i: u8) { ... }
    fn write_u16(&mut self, i: u16) { ... }
    fn write_u32(&mut self, i: u32) { ... }
    fn write_u64(&mut self, i: u64) { ... }
    fn write_u128(&mut self, i: u128) { ... }
    fn write_usize(&mut self, i: usize) { ... }
    fn write_i8(&mut self, i: i8) { ... }
    fn write_i16(&mut self, i: i16) { ... }
    fn write_i32(&mut self, i: i32) { ... }
    fn write_i64(&mut self, i: i64) { ... }
    fn write_i128(&mut self, i: i128) { ... }
    fn write_isize(&mut self, i: isize) { ... }
    fn write_length_prefix(&mut self, len: usize) { ... }
    fn write_str(&mut self, s: &str) { ... } // 计算字符串 hash
}

// 举例
use std::hash::{DefaultHasher, Hasher};
let mut hasher = DefaultHasher::new();

hasher.write_u32(1989);
hasher.write_u8(11);
hasher.write_u8(9);
hasher.write(b"Huh?");
hasher.write(&[1, 2, 3, 4]);
hasher.write(&[5, 6]);

println!("Hash is {:x}!", hasher.finish());

std::hash::DefaultHasher 类型实现了 std::hash::Hasher trait,它是 HashMap 的 RandomState 的缺省实现。

由于 Hasher 内部包含状态(多次 write,最后 finish() 返回 hash 值),所以对于 HashMap 需要为每一个 key 创建一个 Hasher 来计算它的 hash 值。而 std::hash::BuildHasher trait 就是来创建该 Hasher 对象的:

  • 对于 build_hasher() 返回的 Hasher,相同的输入应该产生相同的 hash 值;
pub trait BuildHasher {
    type Hasher: Hasher;

    // Required method
    fn build_hasher(&self) -> Self::Hasher;

    // Provided method
    fn hash_one<T>(&self, x: T) -> u64
       where T: Hash,
             Self: Sized,
             Self::Hasher: Hasher { ... }
}

std::hash::RandomState struct 实现了 BuildHahser trait, 它是 HashMap 默认的 Hasher:

use std::hash::{BuildHasher, Hasher, RandomState};

let s = RandomState::new();
let mut hasher_1 = s.build_hasher();
let mut hasher_2 = s.build_hasher();

hasher_1.write_u32(8128);
hasher_2.write_u32(8128);

assert_eq!(hasher_1.finish(), hasher_2.finish());

创建 HashMap 时可以指定 Hasher:

use std::collections::HashMap;
use std::hash::RandomState;

let s = RandomState::new(); // RandomState 实现了 Trait std::hash::BuildHasher
let mut map = HashMap::with_hasher(s); // s 需要实现 Trait std::hash::BuildHasher
map.insert(1, 2);

11 std::fmt
#

std::fmt module 提供的宏函数:

  • format!
  • write! // first argument is either a &mut io::Write or a &mut fmt::Write
  • writeln! // same as write but appends a newline
  • print! // the format string is printed to the standard output
  • println! // same as print but appends a newline
  • eprint! // the format string is printed to the standard error
  • eprintln! // same as eprint but appends a newline
  • format_args! // described below.
format!("{1} {} {0} {}", 1, 2); // => "2 1 1 2"

format!("{argument}", argument = "test");   // => "test"
format!("{name} {}", 1, name = 2);          // => "2 1"
format!("{a} {c} {b}", a="a", b='b', c=3);  // => "a 3 b"

use std::fmt;
use std::io::{self, Write};
let mut some_writer = io::stdout();
write!(&mut some_writer, "{}", format_args!("print with a {}", "macro"));

fn my_fmt_fn(args: fmt::Arguments<'_>) {
    write!(&mut io::stdout(), "{args}");
}
my_fmt_fn(format_args!(", or a {} too", "function"));

使用 write!() 宏来实现 Display trait:

use std::io::Write;

let mut w = Vec::new();
// write!() 返回 std::io::Result, 不能忽略。
let _ = write!(&mut w, "Hello {}!", "world");

格式化:

  1. 字符串默认是左对齐,数字是右对齐
  2. 位置参数和命名参数可以混用,但命名参数必须位于位置参数后面;
  3. 命名参数的名称也可以是上下文中的变量名称;

语法:

format_string := text [ maybe_format text ] *
maybe_format := '{' '{' | '}' '}' | format
format := '{' [ argument ] [ ':' format_spec ] [ ws ] * '}'
argument := integer | identifier

format_spec := [[fill]align][sign]['#']['0'][width]['.' precision]type
fill := character
align := '<' | '^' | '>'
sign := '+' | '-'
width := count
precision := count | '*'
type := '' | '?' | 'x?' | 'X?' | identifier
count := parameter | integer
parameter := argument '$'

示例:

format!("Hello");                 // => "Hello"
format!("Hello, {}!", "world");   // => "Hello, world!"
format!("The number is {}", 1);   // => "The number is 1"
format!("{:?}", (3, 4));          // => "(3, 4)"
format!("{value}", value=4);      // => "4"
let people = "Rustaceans";
format!("Hello {people}!");       // => "Hello Rustaceans!"
format!("{} {}", 1, 2);           // => "1 2"
format!("{:04}", 42);             // => "0042" with leading zeros
format!("{:#?}", (100, 200));     // #? 表示 pretty print
// => "(
//       100,
//       200,
//     )"

format!("{name} {}", 1, name = 2);          // => "2 1"
format!("{a} {c} {b}", a="a", b='b', c=3);  // => "a 3 b"

// width
// All of these print "Hello x    !"
println!("Hello {:5}!", "x");
println!("Hello {:1$}!", "x", 5);  // N$ 表示取第 N 个参数值为宽度,所以 1$ 取 5
println!("Hello {1:0$}!", 5, "x"); // 0$ 取值 5
println!("Hello {:width$}!", "x", width = 5); // width$ 取值变量 width
let width = 5;
println!("Hello {:width$}!", "x");

// align
assert_eq!(format!("Hello {:<5}!", "x"),  "Hello x    !");
assert_eq!(format!("Hello {:-<5}!", "x"), "Hello x----!");
assert_eq!(format!("Hello {:^5}!", "x"),  "Hello   x  !");
assert_eq!(format!("Hello {:>5}!", "x"),  "Hello     x!");

// Sign/#/0
assert_eq!(format!("Hello {:+}!", 5), "Hello +5!"); // + 显示正负号前缀
assert_eq!(format!("{:#x}!", 27), "0x1b!"); // #x 显示 0x 前缀
assert_eq!(format!("Hello {:05}!", 5),  "Hello 00005!"); // 0 表示补 0
assert_eq!(format!("Hello {:05}!", -5), "Hello -0005!");
assert_eq!(format!("{:#010x}!", 27), "0x0000001b!");

// 精度
//  .5 精度
println!("Hello {0} is {1:.5}", "x", 0.01);
// .N$ 表示取第 N 个位置参数值为精度
println!("Hello {1} is {2:.0$}", 5, "x", 0.01);
println!("Hello {0} is {2:.1$}", "x", 5, 0.01);

// .* 有 3 种风格(建议使用 .N$ 风格):
// 1. {:.*} 表示: 下一个 position arg 为精度(5),后续为要打印的值(0.01)
println!("Hello {} is {:.*}",    "x", 5, 0.01);
// 2. {arg:.*} 表示:先取 arg 为要打印的值(0.01),再去下一个 position arg 为精度(5)
println!("Hello {1} is {2:.*}",  5, "x", 0.01);
println!("Hello {} is {2:.*}",   "x", 5, 0.01);
// 3. 直接指定两个参数
println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01);

// 转义
assert_eq!(format!("Hello {{}}"), "Hello {}");
assert_eq!(format!("{{ Hello"), "{ Hello");

Fromat trait

  • nothing ⇒ Display
  • ? ⇒ Debug
  • x? ⇒ Debug with lower-case hexadecimal integers
  • X? ⇒ Debug with upper-case hexadecimal integers
  • o ⇒ Octal
  • x ⇒ LowerHex
  • X ⇒ UpperHex
  • p ⇒ Pointer
  • b ⇒ Binary
  • e ⇒ LowerExp
  • E ⇒ UpperExp

Debug 只能通过 #[derive(Debug)] 宏由编译器自动实现,而 Display trait 需要自己手动实现。

对于实现了 Display 的 trait ,Rust 自动为其实现 ToString trait ,它的 to_string() 方法返回一个 String。例如实现 std::error:Error trait 时,同时需要实现 Display + Debug,所以所有 Error 对象都可以直接显示和生成字符串。

let i = 5;
let five = String::from("5");
assert_eq!(five, i.to_string());

12 std::mem
#

std::mem module 提供了各类型的 size/aligment/take/replace() 等操作函数:

  • align_of::<T>()/align_of_val(&v): 返回类型 T,或 v(需要传入借用类型) 指向的对象的对齐方式;
  • drop(T): drop 对象 T, 执行 T 实现的 Drop trait 方法;(不能直接调用对象实现的 Drop 的 drop() 方法);
  • forget(T): drop 对象 T, 但是不执行它的 Drop trait 方法;

replace/swap/take() 用来获取或替换 array/vec 中没有实现 Copy trait 的单个元素:

  • replace(&mut T1, T2): 用传入的 T2 值替换 &mut T1 对象,返回旧的 T1 值; 适用于替换不能 partial move 的对象(使用 &mut 来替换旧值)。
  • swap(&mut T1, &mut T2): 交换 T1 和 T2 的值;
  • take(&mut T1): 返回 T1 的值,将原来 &mut T1 的值用 T1 的 Default 值填充
use std::mem;

pub const fn align_of<T>() -> usize
pub fn align_of_val<T>(val: &T) -> usize where T: ?Sized // 返回引用的 T 值类型的内存对齐要求
assert_eq!(4, mem::align_of::<i32>());
assert_eq!(4, mem::align_of_val(&5i32)); // 存入 val 的引用

// 回收 T 值(其实是获得 T 的所有权后丢弃)
pub fn drop<T>(_x: T)

// Takes ownership and “forgets” about the value without running its destructor.
pub const fn forget<T>(t: T)
let file = File::open("foo.txt").unwrap();
mem::forget(file);

// Moves src into the referenced dest, returning the previous dest value.
pub fn replace<T>(dest: &mut T, src: T) -> T
use std::mem;
let mut v: Vec<i32> = vec![1, 2];
// 新的值替换传入的 &mut 值 v,返回 v 对象。
let old_v = mem::replace(&mut v, vec![3, 4, 5]);
assert_eq!(vec![1, 2], old_v);
assert_eq!(vec![3, 4, 5], v);
// replace 的场景场景是替换容器中的元素
impl<T> Buffer<T> {
    fn replace_index(&mut self, i: usize, v: T) -> T {
        mem::replace(&mut self.buf[i], v) // 可以避免从 &mut self 中转移所有权而报错
    }
}

// 返回指定类型 T 的大小
pub const fn size_of<T>() -> usize
// Some primitives
assert_eq!(4, mem::size_of::<i32>());
assert_eq!(8, mem::size_of::<f64>());
assert_eq!(0, mem::size_of::<()>());
assert_eq!(8, mem::size_of::<[i32; 2]>());
assert_eq!(12, mem::size_of::<[i32; 3]>());
assert_eq!(0, mem::size_of::<[i32; 0]>());
assert_eq!(mem::size_of::<&i32>(), mem::size_of::<*const i32>());
assert_eq!(mem::size_of::<&i32>(), mem::size_of::<Box<i32>>());
assert_eq!(mem::size_of::<&i32>(), mem::size_of::<Option<&i32>>());
assert_eq!(mem::size_of::<Box<i32>>(), mem::size_of::<Option<Box<i32>>>());

//  返回借用指向的 T 的大小,和 size_of::<T>() 的区别是该方法也适用于动态类型大小,如 [T] 和 trait
//  object
pub fn size_of_val<T>(val: &T) -> usize where T: ?Sized
assert_eq!(4, mem::size_of_val(&5i32));
let x: [u8; 13] = [0; 13];
let y: &[u8] = &x;
assert_eq!(13, mem::size_of_val(y));

// Swaps the values at two mutable locations, without deinitializing either one.
pub fn swap<T>(x: &mut T, y: &mut T)
let mut x = 5;
let mut y = 42;
mem::swap(&mut x, &mut y);
assert_eq!(42, x);
assert_eq!(5, y);

// Replaces dest with the default value of T, returning the previous dest value.
pub fn take<T>(dest: &mut T) -> T where T: Default,
let mut v: Vec<i32> = vec![1, 2];
let old_v = mem::take(&mut v);
assert_eq!(vec![1, 2], old_v);
assert!(v.is_empty());

align_of/align_of_val() 以及 size_of/size_of_val() 都提供了 _val 版本,主要的使用场景是获得 动态类型 对应的实际类型的对齐方式或大小:如 [T] 和 trait object。

std::mem::offset_of!() 宏返回 struct filed 或 enum variant field 的偏移量:

#![feature(offset_of_enum, offset_of_nested)]
#[repr(C)]
struct FieldStruct {
    first: u8,
    second: u16,
    third: u8
}
assert_eq!(mem::offset_of!(FieldStruct, first), 0);
assert_eq!(mem::offset_of!(FieldStruct, second), 2);
assert_eq!(mem::offset_of!(FieldStruct, third), 4);

#[repr(C)]
struct NestedA {
    b: NestedB
}
#[repr(C)]
struct NestedB(u8);
assert_eq!(mem::offset_of!(NestedA, b.0), 0);

#[repr(u8)]
enum Enum {
    A(u8, u16),
    B { one: u8, two: u16 },
}
assert_eq!(mem::offset_of!(Enum, A.0), 1);
assert_eq!(mem::offset_of!(Enum, B.two), 2);
assert_eq!(mem::offset_of!(Option<&u8>, Some.0), 0);

std::mem::discriminant() 返回 enum variant 的 tag 值,也可以使用 as 运算符来获取:

enum Foo {
    A(&'static str),
    B(i32),
    C(i32)
}
assert_eq!(mem::discriminant(&Foo::A("bar")), mem::discriminant(&Foo::A("baz")));
assert_eq!(mem::discriminant(&Foo::B(1)), mem::discriminant(&Foo::B(2)));
assert_ne!(mem::discriminant(&Foo::B(3)), mem::discriminant(&Foo::C(3)));

enum Enum {
    Foo,
    Bar,
    Baz,
}
assert_eq!(0, Enum::Foo as isize);
assert_eq!(1, Enum::Bar as isize);
assert_eq!(2, Enum::Baz as isize);

类型大小:

  1. () = 1;
  2. bool = 1;
  3. char = 4;
  4. *const T, &T, Box<T>, Option<&T>, 和 Option<Box<T>> 具有相同大小, 如果 T 是 Sized, 则这些类型大小和 usize 一致;
  5. [T; n]: 大小为 n * size_of::<T>()
  6. Enums: 如果 Enum 的各 variant 除了 tag 外都没有 data,则大小和 C enum 一致;
  7. Unions: 取决于最大 field 的大小;

对象类型的大小还受 #[repr(C)], repr(align(N)) 和 #[repr(u16)] 等属性的影响。

std::mem::transmute() :将 Src 类型的 value 解释为 Dst 类型的 value,这里的解释是 bit 级别 ,Src和 Dst类型 必须具有相同的长度 ,否则编译错误。将 Src value bits 级别的 copy 为 Dst 的 value,然后 forget Src value(drop 但不调用它的 Drop trait)。

两个常用场景:

  1. 在 *const 指针和函数指针间转换;
  2. 扩充或缩短 lifetime;
pub const unsafe extern "rust-intrinsic" fn transmute<Src, Dst>(src: Src) -> Dst

let a: i64 = 42;
let a_ptr: *const i64 = &a as *const i64;
// 将 a_ptr 解释为 usize 后,可以对指针值执行算术运算
let a_addr: usize = unsafe {std::mem::transmute(a_ptr)};
println!("a: {} ({:p}...0x{:x})", a, a_ptr, a_addr + 7);

// transmute 的两个常用场景:
//
// 1. 在 *const 指针和函数指针间转换
fn foo() -> i32 { 0 }
// Crucially, we `as`-cast to a raw pointer before `transmute`ing to a function pointer.
// This avoids an integer-to-pointer `transmute`, which can be problematic.
// Transmuting between raw pointers and function pointers (i.e., two pointer types) is fine.
let pointer = foo as *const ();
let function = unsafe {
    std::mem::transmute::<*const (), fn() -> i32>(pointer)
};
assert_eq!(function(), 0);

// 2. 扩充或缩短 lifetime
struct R<'a>(&'a i32);
unsafe fn extend_lifetime<'b>(r: R<'b>) -> R<'static> {
    std::mem::transmute::<R<'b>, R<'static>>(r)
}
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)
}

其它使用 transmute 场景,可以使用其它更安全的 APIs 来代替:

// 将 4 个 u8 解释为 u32
let raw_bytes = [0x78, 0x56, 0x34, 0x12];
let num = unsafe {
    std::mem::transmute::<[u8; 4], u32>(raw_bytes)
};
// use `u32::from_ne_bytes` instead
let num = u32::from_ne_bytes(raw_bytes);
// or use `u32::from_le_bytes` or `u32::from_be_bytes` to specify the endianness
let num = u32::from_le_bytes(raw_bytes);
assert_eq!(num, 0x12345678);
let num = u32::from_be_bytes(raw_bytes);
assert_eq!(num, 0x78563412);


// 将指针转为 usize
let ptr = &0;
let ptr_num_transmute = unsafe {
    std::mem::transmute::<&i32, usize>(ptr)
};
let ptr_num_cast = ptr as *const i32 as usize; // 使用更安全的 as 转换


// 将 *mut T 转换为 &mut T
let ptr: *mut i32 = &mut 0;
let ref_transmuted = unsafe {
    std::mem::transmute::<*mut i32, &mut i32>(ptr)
};
// Use a reborrow instead
let ref_casted = unsafe { &mut *ptr };

// 将 &mut T 转换为 &mut U:
let ptr = &mut 0;
let val_transmuted = unsafe {
    std::mem::transmute::<&mut i32, &mut u32>(ptr)
};
// Now, put together `as` and reborrowing - note the chaining of `as` `as` is not transitive
let val_casts = unsafe { &mut *(ptr as *mut i32 as *mut u32) };

// 将 &str 转换为 &[u8]
let slice = unsafe { std::mem::transmute::<&str, &[u8]>("Rust") };
assert_eq!(slice, &[82, 117, 115, 116]);
let slice = "Rust".as_bytes();
assert_eq!(slice, &[82, 117, 115, 116]);
assert_eq!(b"Rust", &[82, 117, 115, 116]);

pub const unsafe fn transmute_copy<Src, Dst>(src: &Src) -> DstInterprets src as having type &Dst, and then reads src without moving the contained value.

transmute_copy(&Src) -> Dst 函数是将 Src 类型解释为 Dst 类型,然后将 Src 的内容 byte copy 形成 Dst 类型对象,Src 和 Dst 不共享内存,也没有像 transmute() 那样要求 Src 和 Dst 大小必须一致:

use std::mem;

#[repr(packed)]
struct Foo {
    bar: u8,
}

let foo_array = [10u8];

unsafe {
    // Copy the data from 'foo_array' and treat it as a 'Foo'
    let mut foo_struct: Foo = mem::transmute_copy(&foo_array);
    assert_eq!(foo_struct.bar, 10);

    // Modify the copied data
    foo_struct.bar = 20;
    assert_eq!(foo_struct.bar, 20);
}

// The contents of 'foo_array' should not have changed
assert_eq!(foo_array, [10]);

std::mem::zeroed: pub const unsafe fn zeroed<T>() -> T :返回用 0 填充的 T 值(建议使用 std::mem::MaybeUninit::<T>::zerod()):

use std::mem;
let x: i32 = unsafe { mem::zeroed() };
assert_eq!(0, x);

// 错误的用法
let _x: &i32 = unsafe { mem::zeroed() }; // Undefined behavior!
let _y: fn() = unsafe { mem::zeroed() }; // And again!

Struct std::mem::ManuallyDrop<T> 获得 T 对象所有权,阻止编译器自动调用 T 的 Drop,而是在需要时手动 Drop 对象:

let mut x = std::mem::ManuallyDrop::new(String::from("Hello World!"));
x.truncate(5);
assert_eq!(*x, "Hello");
// But `Drop` will not be run here

12.1 MaybeUninit
#

Rust 编译器在创建对象时为其分配内存后会自动进行初始化,然后才能借用对象,否则就是 undefined behavior (对于未初始化的内存区域,每次 read 的结果可能不一致)。

如在创建一个 bool 类型对象时,必须设置为 true 或 false,如果未初始化则 UB:

let x: &i32 = unsafe { mem::zeroed() }; // undefined behavior! ⚠
// The equivalent code with `MaybeUninit<&i32>`:
let x: &i32 = unsafe { MaybeUninit::zeroed().assume_init() }; // undefined behavior! ⚠

let b: bool = unsafe { mem::uninitialized() }; // undefined behavior!
// The equivalent code with `MaybeUninit<bool>`:
let b: bool = unsafe { MaybeUninit::uninit().assume_init() }; // undefined behavior! ⚠

std::mem::MaybeUninit :使用 unsafe 代码来使用未初始化内存区域。编译器不对 MaybeUninit<T> 进行 runtime tracking 和 safety check, Drop MaybeUninit<T> 对象时也 不会调用 T 的 drop() 方法。

创建 MaybeUninit<T> 对象:

  • new(T):使用传入的 T 值来初始化内存;
  • uninit(): 为 T 类型分配一块未初始化内存;
  • uninit_array<const N: usize>():返回一个 N 个元素的未初始化数组内存,再将它转换为 slice 来使用;
  • pub const fn zeroed() -> MaybeUninit<T> : 返回一个用 zero 填充的对象 T;

对 MaybeUninit<T> 值进行修改:

  • write(value): 使用 value 设置 MaybeUninit<T> 内存数据,不 Drop 内存地址的数据(所有多次重复调用时可能导致内存泄露),返回 &mut T;
  • as_ptr/as_mut_ptr(): 转换为 raw pointer,可以使用它的 read/write 等方法来读写内存;

返回 MaybeUninit<T> 中的 T 值:

  • assume_init(self) -> T:返回内存保存的 T 类型值=( =转移 该内存区域所有权);
  • assume_init_read(&self) -> T:通过内存区域 bitwise copy(不管 T 类型是否实现 Copy)来构建一个 T 类型值(内部通过 std::ptr::read() 实现的 浅拷贝 ,共享同一个内存区域 )。

由于多次调用 assume_init_read() 返回的 T 共享同一个内存区域 ,当各 T 被 Drop 时会导致 多次内存释放的错误 ,建议使用 assume_init() 来返回包含对应内存区域所有权的 T 值,从而避免多次释放。

pub union MaybeUninit<T> {
    /* private fields */
}

impl<T> MaybeUninit<T>

// Creates a new MaybeUninit<T> initialized with the given value. It is safe to
// call assume_init
// on the return value of this function.
// Note that dropping a MaybeUninit<T> will never call T’s drop code. It is
// your responsibility to make sure T gets dropped if it got initialized.
pub const fn new(val: T) -> MaybeUninit<T>
    use std::mem::MaybeUninit;
    let v: MaybeUninit<Vec<u8>> = MaybeUninit::new(vec![42]);

// Creates a new MaybeUninit<T> in an uninitialized state.
pub const fn uninit() -> MaybeUninit<T>
    use std::mem::MaybeUninit;
    let v: MaybeUninit<String> = MaybeUninit::uninit();

pub fn uninit_array<const N: usize>() -> [MaybeUninit<T>; N]
    #![feature(maybe_uninit_uninit_array, maybe_uninit_slice)]
    use std::mem::MaybeUninit;
    extern "C" {
        fn read_into_buffer(ptr: *mut u8, max_len: usize) -> usize;
    }
    /// Returns a (possibly smaller) slice of data that was actually read
    fn read(buf: &mut [MaybeUninit<u8>]) -> &[u8] {
        unsafe {
            let len = read_into_buffer(buf.as_mut_ptr() as *mut u8, buf.len());
            MaybeUninit::slice_assume_init_ref(&buf[..len])
        }
    }
    let mut buf: [MaybeUninit<u8>; 32] = MaybeUninit::uninit_array();
    let data = read(&mut buf);

// Creates a new MaybeUninit<T> in an uninitialized state, with the memory
// being filled with 0 bytes.
pub const fn zeroed() -> MaybeUninit<T>
    let x = MaybeUninit::<(u8, bool)>::zeroed();
    let x = unsafe { x.assume_init() };
    assert_eq!(x, (0, false));

pub fn write(&mut self, val: T) -> &mut T
    let mut x = MaybeUninit::<Vec<u8>>::uninit();
    {
        let hello = x.write((&b"Hello, world!").to_vec());
        // Setting hello does not leak prior allocations, but drops them
        *hello = (&b"Hello").to_vec();
        hello[0] = 'h' as u8;
    }
    // x is initialized now:
    let s = unsafe { x.assume_init() };
    assert_eq!(b"hello", s.as_slice());


// Gets a pointer to the contained value. Reading from this pointer or turning
// it into a reference
// is undefined behavior unless the MaybeUninit<T> is initialized.
pub const fn as_ptr(&self) -> *const T
pub fn as_mut_ptr(&mut self) -> *mut T
    use std::mem::MaybeUninit;
    let mut x = MaybeUninit::<Vec<u32>>::uninit();
    x.write(vec![0, 1, 2]);
    // Create a reference into the `MaybeUninit<T>`. This is okay because we
    // initialized it.
    let x_vec = unsafe { &*x.as_ptr() };
    assert_eq!(x_vec.len(), 3);

// Extracts the value from the MaybeUninit<T> container. This is a great way
// to ensure that the
// data will get dropped, because the resulting T is subject to the usual drop
// handling.
pub const unsafe fn assume_init(self) -> T // 转移所有权,返回 T
pub const unsafe fn assume_init_read(&self) -> T // 转移所有权
pub unsafe fn assume_init_drop(&mut self) // in-place Drop 包含的值
pub const unsafe fn assume_init_ref(&self) -> &T // 返回 &T,不转移所有权
pub unsafe fn assume_init_mut(&mut self) -> &mut T // 返回 &mut T
pub unsafe fn array_assume_init<const N: usize>(array: [MaybeUninit<T>; N] )
   -> [T; N] // 返回 array
pub unsafe fn slice_assume_init_ref(slice: &[MaybeUninit<T>]) -> &[T]
pub unsafe fn slice_assume_init_mut(slice: &mut [MaybeUninit<T>]) -> &mut [T]
    let mut x = MaybeUninit::<bool>::uninit();
    x.write(true);
    let x_init = unsafe { x.assume_init() };
    assert_eq!(x_init, true);

pub fn slice_as_ptr(this: &[MaybeUninit<T>]) -> *const T
pub fn slice_as_mut_ptr(this: &mut [MaybeUninit<T>]) -> *mut T

pub fn write_slice<'a>(this: &'a mut [MaybeUninit<T>], src: &[T]) ->
    &'a mut [T] where T: Copy
pub fn write_slice_cloned<'a>( this: &'a mut [MaybeUninit<T>], src: &[T] ) ->
    &'a mut [T] where T: Clone

pub fn as_bytes(&self) -> &[MaybeUninit<u8>]
pub fn as_bytes_mut(&mut self) -> &mut [MaybeUninit<u8>]
pub fn slice_as_bytes(this: &[MaybeUninit<T>]) -> &[MaybeUninit<u8>]
pub fn slice_as_bytes_mut(this: &mut [MaybeUninit<T>]) -> &mut [MaybeUninit<u8>]

MaybeUninit<T> 为 T 分配未初始化的内存,并拥有该内存区域, 但不做任何优化和操作:

  • 先使用 as_ptr()/as_mut_ptr() 转换为 raw pointer,将它传递给 FFI 函数使用;
    • C 很常见的情况是:给函数传递一个内存指针, 函数内的逻辑来修改指针指向的内容。
  • 或者使用 MaybeUninit::write() 或者 raw pointer 的 read/write() 来对未初始化内存区域进行读写;
  • 然后调用 MaybeUninit.assume_init() 来将内存区域标记为已初始化,转移返回 T 的值;
// 分配一个保存 &i32 类型的内存,但是不初始化
let mut x = MaybeUninit::<&i32>::uninit();
// 使用 MaybeUninit 类型的 write() 方法写入数据
x.write(&0);
// 提取数据,只有当对 x 做过初始化处理后才有意义
let x = unsafe { x.assume_init() };

// undefined behavior :x 未被初始化,assume_init() 返回的 x 值是 UB 的。
let x = MaybeUninit::<Vec<u32>>::uninit();
let x_init = unsafe { x.assume_init() };

// 传入未初始化内存区域的 raw pointer,然后使用它的 write() 方法来填充内存区域的值
unsafe fn make_vec(out: *mut Vec<i32>) {
    // `write` does not drop the old contents, which is important.
    out.write(vec![1, 2, 3]);
}
// 未指定类型,由编译器自动推导。
let mut v = MaybeUninit::uninit();
unsafe { make_vec(v.as_mut_ptr()); }
let v = unsafe { v.assume_init() };
assert_eq!(&v, &[1, 2, 3]);


// 使用 MaybeUninit 来初始化 array elem:
let data = {
    // Create an uninitialized array of `MaybeUninit`. The `assume_init` is
    // safe because the type we are claiming to have initialized here is a
    //  bunch of `MaybeUninit`s, which do not require initialization.
    let mut data: [MaybeUninit<Vec<u32>>; 1000] = unsafe {
        MaybeUninit::uninit().assume_init()
    };
    for elem in &mut data[..] {
        elem.write(vec![42]);
    }
    // Everything is initialized. Transmute the array to the initialized type.
    unsafe { mem::transmute::<_, [Vec<u32>; 1000]>(data) }
};
assert_eq!(&data[0], &[42]);

// 使用 MaybeUninit 来初始化 struct field
#[derive(Debug, PartialEq)]
pub struct Foo {
    name: String,
    list: Vec<u8>,
}
let foo = {
    let mut uninit: MaybeUninit<Foo> = MaybeUninit::uninit();
    let ptr = uninit.as_mut_ptr();

    // Initializing the `name` field
    // Using `write` instead of assignment via `=` to not call `drop` on the
    // old, uninitialized value.
    unsafe { std::ptr::addr_of_mut!((*ptr).name).write("Bob".to_string()); }

    // Initializing the `list` field
    // If there is a panic here, then the `String` in the `name` field leaks.
    unsafe { std::ptr::addr_of_mut!((*ptr).list).write(vec![0, 1, 2]); }
    unsafe { uninit.assume_init() }
};
assert_eq!( foo, Foo { name: "Bob".to_string(), list: vec![0, 1, 2]});

MaybeUninit<T>: 保证和 T 同样的大小、对齐和 ABI:

use std::mem::{MaybeUninit, size_of, align_of};

assert_eq!(size_of::<MaybeUninit<u64>>(), size_of::<u64>());
assert_eq!(align_of::<MaybeUninit<u64>>(), align_of::<u64>());
assert_eq!(size_of::<Option<bool>>(), 1);
assert_eq!(size_of::<Option<MaybeUninit<bool>>>(), 2);

13 std::ptr
#

std::ptr module 提供了一些操作 raw pointer 的函数。raw pointer 类型 *const T 或 *mut T 本身也提供一些操作 raw pointer 的方法。

这个 module 通过 raw pointer 如 *mut T 或 *const T 来存取一个值,这个值的大小如果没有特殊说明,对应的是 std::mem::size_of::<T>() bytes。

使用 std::ptr::addr_of!() 和 std::ptr::addr_of_mut!() 返回 struct field 的地址:

  • packed struct:默认情况下,struct 对象的 field 会通过 pading 来对齐。通过添加 packed attr,可以关闭 struct field padding 对齐。
  • 对于未对齐的 filed,是不能创建引用的,但是通过 addr_of!() 和 addr_of_mut!() 宏是可以创建 未对齐的 raw pointer
#[derive(Debug, Default, Copy, Clone)]
#[repr(C, packed)]
struct S {
    aligned: u8,
    unaligned: u32,
}
let s = S::default();
// not allowed with coercion
let p = std::ptr::addr_of!(s.unaligned);

Rust 的 raw pointer 和引用都是指针,包括两部分:

  1. data pointer:指向 value 内存地址的指针;
  2. 可选的 metadata 指针;

对于编译时可知的固定大小类型(实现了 Sized trait)或 extern 类型的指针,是 thin 指针 , metadata 是 zero-sized 的 () 类型,所以 thin 指针是只占用一个机器字 usize 的变量。

对于动态大小类型(DST),它的指针是 fat 指针 ,metadata 非空。fat usize 大小(data pointer + metadata pointer),如 *const [u8] 或 *mut dyn std::io::Write:

  • For structs whose last field is a DST, metadata is the metadata for the last field
  • For the str type, metadata is the length in bytes as usize
  • For slice types like [T], metadata is the length in items as usize
  • For trait objects like dyn SomeTrait, metadata is DynMetadata<Self> (e.g. DynMetadata<dyn SomeTrait>)

std::ptr::Pointee trait 为任意指针(thin 或 fat pointer)提供 metadata type 信息, Rust 为所有类型实现了该 trait :

  • Metadata 关联类型可能是 () or usize or DynMetadata<_> 类型;

    • (): 对应没有 Metadata 的 thin 指针;
    • usize:对应 lenght in bytes(如 &str)或 length in items(如 [T]);
    • DynMetadata: 对应 trait object;
  • raw pointer 的 .to_raw_parts() 方法返回对象的 data pointer 和包裹 Metadata 类型的 Pointee 对象;

  • std::ptr::metadata() 方法返回对象的 Metadata 类型对象;

  • std::ptr::from_raw_parts()/from_raw_parts_mut() 函数来使用 data pointer 和 Metadata 类型对象创建 raw pointer

// raw pointer 的 to_raw_parts() 方法返回 Metadata 信息
pub trait Pointee {
    // Metadata 的实例化类型:() 或 usize 或 DynMetadata<Dyn>
    type Metadata: Copy + Send + Sync + Ord + Hash + Unpin;
}

pub struct DynMetadata<Dyn> where Dyn: ?Sized

// Decompose a (possibly wide) pointer into its data pointer and
// metadata components.
pub fn to_raw_parts(self) -> (*const (), <T as Pointee>::Metadata)

// Forms a (possibly-wide) raw pointer from a data pointer and metadata.
pub fn from_raw_parts<T>(data_pointer: *const (),
  metadata: <T as Pointee>::Metadata) -> *const T where T: ?Sized,

Struct std::ptr::DynMetadata 是 trait object 的 metadata,It is a pointer to a vtable (virtual call table) that represents all the necessary information to manipulate the concrete type stored inside a trait object. The vtable notably it contains:

  • type size
  • type alignment
  • a pointer to the type’s drop_in_place impl (may be a no-op for plain-old-data)
  • pointers to all the methods for the type’s implementation of the trait

Rust 的 type coercion 为引用和 raw pointer 提供了隐式自动转换(也可以使用 as 运算符来对type coercion 显式转换):

  1. &mut T to &T <- 可变引用可以转换为不可变引用
  2. *mut T to *const T <– 可变 raw pointer 可以转换为不可变 raw pointer
  3. &T to *const T <– 不可变引用 可以转换为 不可变 raw pointer
  4. &mut T to *mut T <– 可变引用 可以转换为 可变 raw pointer
// &mut T -> &T
let r: &i32 = &mut 1i32;

// &T -> *const T
let rp: *const i32 = &1i32;

// &mut T -> *const T 或 *mut T
let r: *const i32 = &mut 1i32;
let r: *mut i32 = &mut 1i32;

// *mut T -> *const T
// 可以使用 as 运算符, 对支持 type coercion 的变量类型进行转换.
let rp: *const i32 = &mut 1i32 as *mut i32 as *const i32;

std::ptr module 提供的函数列表(大部分函数也是 *const T 或 *mut T 类型的方法):

addr_eq
Compares the addresses of the two pointers for equality, ignoring any metadata in fat pointers.
copy
Copies count * size_of::<T>() bytes from src to dst. The source and destination may overlap.
copy_nonoverlapping
Copies count * size_of::<T>() bytes from src to dst. The source and destination must not overlap.
drop_in_place
Executes the destructor (if any) of the pointed-to value.
eq
Compares raw pointers for equality.
from_mut
Convert a mutable reference to a raw pointer.
from_ref
Convert a reference to a raw pointer.
hash
Hash a raw pointer.
null
Creates a null raw pointer.
null_mut
Creates a null mutable raw pointer.
read
Reads the value from src without moving it. This leaves the memory in src unchanged.
read_unaligned
Reads the value from src without moving it. This leaves the memory in src unchanged.
read_volatile
Performs a volatile read of the value from src without moving it. This leaves the memory in src unchanged.
replace
Moves src into the pointed dst, returning the previous dst value.
slice_from_raw_parts
Forms a raw slice from a pointer and a length.
slice_from_raw_parts_mut
Performs the same functionality as slice_from_raw_parts, except that a raw mutable slice is returned, as opposed to a raw immutable slice.
swap
Swaps the values at two mutable locations of the same type, without deinitializing either.
swap_nonoverlapping
Swaps count * size_of::<T>() bytes between the two regions of memory beginning at x and y. The two regions must not overlap.
write
Overwrites a memory location with the given value without reading or dropping the old value.

write_bytes Sets count * size_of::<T>() bytes of memory starting at dst to val.

write_unaligned Overwrites a memory location with the given value without reading or dropping the old value.

write_volatile Performs a volatile write of a memory location with the given value without reading or dropping the old value.

from_exposed_addrExperimental Convert an address back to a pointer, picking up a previously ‘exposed’ provenance.

from_exposed_addr_mutExperimental Convert an address back to a mutable pointer, picking up a previously ‘exposed’ provenance.

from_raw_partsExperimental Forms a (possibly-wide) raw pointer from a data pointer and metadata.

from_raw_parts_mutExperimental Performs the same functionality as from_raw_parts, except that a raw *mut pointer is returned, as opposed to a raw *const pointer.

invalidExperimental Creates an invalid pointer with the given address.

invalid_mutExperimental Creates an invalid mutable pointer with the given address.

metadataExperimental Extract the metadata component of a pointer.

std::ptr::read()/write() 函数:

  1. std::ptr::read(): pub const unsafe fn read<T>(src: *const T) -> T ,创建 src 的一个 bitwise copy 值(返回的 T 和 src 指向 同一个内存区域 ),而不管 T 是否实现了 Copy,=不影响和移动 src 内存中的内容= 。同时 src 和返回的 T 值内存是独立的。
    • 如果 T 没有实现 Copy,则 src 和返回的 T 由于指向同一个内存区域,在返回的 T 对象被 drop 后,后续如果再释放 src,会出现 多重释放的问题
  2. std::ptr::write(): pub unsafe fn write<T>(dst: *mut T, src: T) , write() can be used to overwrite data without causing it to be dropped. 不会 drop dst 内容,同时将 src 所有权转移到 dst
let x = 12;
let y = &x as *const i32;
unsafe {
    assert_eq!(std::ptr::read(y), 12);
}

use std::ptr;
fn swap<T>(a: &mut T, b: &mut T) {
    unsafe {
        // Create a bitwise copy of the value at `a` in `tmp`.
        let tmp = ptr::read(a);

        // Exiting at this point (either by explicitly returning or by calling
        // a function which panics) would cause the value in `tmp` to be
        // dropped while the same value is still referenced by `a`. This could
        // trigger undefined behavior if `T` is not `Copy`.

        // Create a bitwise copy of the value at `b` in `a`.  This is safe
        // because mutable references cannot alias.
        ptr::copy_nonoverlapping(b, a, 1);

        // As above, exiting here could trigger undefined behavior because the
        // same value is referenced by `a` and `b`.

        // Move `tmp` into `b`.
        ptr::write(b, tmp);

        // `tmp` has been moved (`write` takes ownership of its second
        // argument), so nothing is dropped implicitly here.

        // tmp 和 a 虽然指向同一个内存区域,但由于 tmp 已经被 write() 转移所有权,
        // 所以这里不会释放 tmp。不会有双重释放的问题。
    }
}
let mut foo = "foo".to_owned();
let mut bar = "bar".to_owned();
swap(&mut foo, &mut bar);
assert_eq!(foo, "bar");
assert_eq!(bar, "foo");

*src = value; 会 Drop src 处的值,而 std::ptr::write() 可以在重写值的同时不 Drop 原来的值。

use std::ptr;

let mut s = String::from("foo");
unsafe {
    // s2 和 s 共享相同的内存区域
    let mut s2: String = ptr::read(&s);
    assert_eq!(s2, "foo");

    // 向 s2 赋值时,会 Drop 对应内存区域的原始值,所以 s 不能再使用(因其内存已经被释放)
    s2 = String::default();
    assert_eq!(s2, "");

    // Assigning to `s` would cause the old value to be dropped again,
    // resulting in undefined behavior.
    // s = String::from("bar"); // ERROR

    // ptr::write() 覆盖 s 中的值但不 Drop 它,所以 OK。
    ptr::write(&mut s, String::from("bar"));
}
assert_eq!(s, "bar");

异常的情况:s 和 spr 都指向同一个内存区域, 会导致双重释放 ,从而出错。

let mut s = "abcd".to_string();
let sp = &mut s as *mut String;
let mut spr = unsafe {std::ptr::read(sp)};
spr.push_str(" test");
println!("s {}, spr {}", s, spr);

// 报错:
// s abcd, spr abcd test
// foo(85413,0x1f2d40f40) malloc: *** error for object 0x600003238060:
// pointer being freed was not allocated
// foo(85413,0x1f2d40f40) malloc: *** set a breakpoint in malloc_error_break
// to debug

std::ptr::write() 的注意点:

  1. 不会 drop dst,有可能导致 dst 指向的内存区域内存泄露;
  2. 不会 drop src,而是将 src 所有权转移到 dst。

14 std::process
#

std::process module 提供的函数:

  • abort : Terminates the process in an abnormal fashion.
  • exit : Terminates the current process with the specified exit code.
  • id : Returns the OS-assigned process identifier associated with this process.

std::process::Command::new() 创建一个 Command, 默认的行为如下:

  1. No arguments to the program
  2. Inherit the current process’s environment
  3. Inherit the current process’s working directory
  4. Inherit stdin/stdout/stderr for spawn() or status(), but create pipes for output()
    • 对于 spawn()/status() 默认是继承父进程的 stdin/stdout/stderr;
    • 对于 output() 则会创建 pipe,这样后续可以获得 Output 对象的 stdout 和 stderr。

在执行 Command 前, 可以进一步设置子进程的参数,工作目录,环境变量等:

// 创建要执行的 program
pub fn new<S: AsRef<OsStr>>(program: S) -> Command

// 设置 program 参数,一次只能设置一个参数
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Command
pub fn args<I, S>(&mut self, args: I) -> &mut Command
    where I: IntoIterator<Item = S>, S: AsRef<OsStr>,

// 设置和清理环境变量
pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Command
    where K: AsRef<OsStr>, V: AsRef<OsStr>,
pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Command
where
    I: IntoIterator<Item = (K, V)>,
    K: AsRef<OsStr>,
    V: AsRef<OsStr>,
pub fn env_remove<K: AsRef<OsStr>>(&mut self, key: K) -> &mut Command
pub fn env_clear(&mut self) -> &mut Command

// 设置工作目录
pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Command

// 设置子进程的 stdin/stdout/stderr
//
// cfg 是可以转换为 Stdio 的类型, 例如 Stdio::null(), Stdio::inherit(),
// Stdio::piped(), Stdio::from()
//
// 对于 spawn()/status() 默认是 Stdio::inherit(), 对于 output() 默认是
// Stdio::piped(), 所以对于 output() 后续可以读取它的 stdout/stderr field 来获得
// Command 的输出。
pub fn stdin<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command
pub fn stdout<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command
pub fn stderr<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command

// 获取进程信息
pub fn get_program(&self) -> &OsStr
pub fn get_args(&self) -> CommandArgs<'_>
pub fn get_envs(&self) -> CommandEnvs<'_>
pub fn get_current_dir(&self) -> Option<&Path>

// 上面创建的 Command 可以被重复执行, 实现复用。

// 执行 Command 并获得输出或退出状态:
// 1. spawn(): 立即返回一个 Child,后续调用 kill/wait/wait_with_output() 结束;
// 2. output/status(): 直接运行,直到结束后返回;
pub fn spawn(&mut self) -> Result<Child>
// Output 中包含 status/stdout/stderr 内容
pub fn output(&mut self) -> Result<Output>
pub fn status(&mut self) -> Result<ExitStatus>

// Linux/Unix 的 CommandExt 方法:
fn create_pidfd(&mut self, val: bool) -> &mut Command
fn uid(&mut self, id: u32) -> &mut Command
fn gid(&mut self, id: u32) -> &mut Command
fn groups(&mut self, groups: &[u32]) -> &mut Command
fn process_group(&mut self, pgroup: i32) -> &mut Command
fn exec(&mut self) -> Error // 准备好所有工作, 执行 execvp 系统调用
// 为命令指定 arg0 参数,即 command name
fn arg0<S>(&mut self, arg: S) -> &mut Command
unsafe fn pre_exec<F>(&mut self, f: F) -> &mut Command
   where F: FnMut() -> Result<()> + Send + Sync + 'static
fn before_exec<F>(&mut self, f: F) -> &mut Command
   where F: FnMut() -> Result<()> + Send + Sync + 'static

// 举例
use std::process::Command;

let output = if cfg!(target_os = "windows") {
    Command::new("cmd")
        .args(["/C", "echo hello"])
        .output()
        .expect("failed to execute process")
} else {
    Command::new("sh")
        .arg("-c")
        .arg("echo hello")
        .args(["-l", "-a"])
	.current_dir("/bin")
    .env_remove("PATH")
	.env_clear()
    .env("PATH", "/bin")
    .stdin(Stdio::null())
    .stdout(Stdio::inherit())
    .stderr(Stdio::null())
    .output() // 立即执行命令并捕获输出,等待命令执行结束后返回
    .expect("failed to execute process")
};
let hello = output.stdout;

use std::process::Command;
use std::io::{self, Write};
let output = Command::new("/bin/cat")
    .arg("file.txt")
    .output()
    .expect("failed to execute process");
println!("status: {}", output.status);
io::stdout().write_all(&output.stdout).unwrap();
io::stderr().write_all(&output.stderr).unwrap();
assert!(output.status.success());

spawn 产生的 Child 子进程并不等待子进程执行结束, 后续需要使用 wait/try_wait/wait_with_output() 等方法来等待执行结束, 或者 kill() 来终止子进程。

Child 类型没有实现 Drop trait,需要自己确保 Child 进程运行结束。

pub struct Child {
    pub stdin: Option<ChildStdin>, // 实现了 Write
    pub stdout: Option<ChildStdout>, // 实现了 Read
    pub stderr: Option<ChildStderr>, // 实现了 Read
    /* private fields */
}
pub fn id(&self) -> u32

// 等待 Child 执行结束(try_wait() 只是检查,不 block)
pub fn kill(&mut self) -> Result<()>
// 等待直到程序退出
pub fn wait(&mut self) -> Result<ExitStatus>
// 不 block 调用线程,而是检查程序是否退出
pub fn try_wait(&mut self) -> Result<Option<ExitStatus>>
// 等待程序结束并捕获所有的 stdout/stderr,需要事先通过 Comand.stdin()/stdout() 设置为
// piped 才行。
pub fn wait_with_output(self) -> Result<Output>

// 例子
use std::process::{Command, Stdio};
use std::io::Write;
let mut child = Command::new("/bin/cat")
    .stdin(Stdio::piped()) // 设置 Child 的 stdin
    .stdout(Stdio::piped())
    .spawn()
    .expect("failed to execute child");
// 需要 stdin.take() 来获得 stdin 所有权,当返回的 stdin 被 Drop 时,程序输入被关闭。
let mut stdin = child.stdin.take().expect("failed to get stdin");
std::thread::spawn(move || {
    stdin.write_all(b"test").expect("failed to write to stdin");
});
let output = child
    .wait_with_output()
    .expect("failed to wait on child");
assert_eq!(b"test", output.stdout.as_slice());

Child 子进程的输入/输出可以通过 Child struct 的 stdin/stdout/stderr field 来获取, 通过读写它们来获得子进程的输入/输出;

  • Child 的 stdin/stdout/stderr 是通过 Command 的 stdin/stdout/stderr() 方法来设置;
  • 在使用 child.stdin/stdout/stderr 时,一般使用 let mut stdin = child.stdin.take().except("xxx") , take() 方法将获取 stdin/stdout/stderr 的所有权,这样当返回的对象被 Drop 时, 相应的文件描述符被关闭
use std::process::{Command, Stdio};

let echo_child = Command::new("echo")
    .arg("Oh no, a tpyo!")
    .stdout(Stdio::piped())
    .spawn()
    .expect("Failed to start echo process");
let echo_out = echo_child.stdout.expect("Failed to open echo stdout");

let mut sed_child = Command::new("sed")
    .arg("s/tpyo/typo/")
    .stdin(Stdio::from(echo_out)) // 从 echo_child 获得输入;
    .stdout(Stdio::piped())
    .spawn()
    .expect("Failed to start sed process");
let output = sed_child.wait_with_output().expect("Failed to wait on sed");
assert_eq!(b"Oh no, a typo!\n", output.stdout.as_slice());

std::process::Stdio 可以作为 Command 的 stdin()/stdout()/stderr() 的输入,来源多样:

impl Stdio

// 将 child 的 stdin/stdout/stderr 输出到 Child 的 stdin/stdout/stderr filed 中,
// 这样父进程(如创建 Command 的主进程)就可以对它们进行读写。
pub fn piped() -> Stdio

// child 从 parent 继承
pub fn inherit() -> Stdio

// 关闭
pub fn null() -> Stdio

// 判断 Stdio 是否是 piped() 生成的.
pub fn makes_pipe(&self) -> bool

// 从其它 Child 的 stdin/stdout/stderr 来创建 Stdio, 从而实现 Child 之间的串联
impl From<ChildStderr> for Stdio
impl From<ChildStdin> for Stdio
impl From<ChildStdout> for Stdio

// 读写文件
impl From<File> for Stdio

// 从终端读写
impl From<Stderr> for Stdio // std::io::Stderr
impl From<Stdout> for Stdio // std::io::Stdout

Unix 还实现了 ExitStatusExt trait,可以返回进程因为 signal 而终止的情况:

pub trait ExitStatusExt: Sealed {
    // Required methods
    fn from_raw(raw: i32) -> Self;

    // If the process was terminated by a signal, returns that signal.
    fn signal(&self) -> Option<i32>;

    fn core_dumped(&self) -> bool;

    // If the process was stopped by a signal, returns that signal.
    fn stopped_signal(&self) -> Option<i32>;

    fn continued(&self) -> bool;
    fn into_raw(self) -> i32;
}

示例:

use std::process::{Command, Stdio};
let output = Command::new("echo")
    .arg("Hello, world!")
    .stdout(Stdio::piped())
    .output()
    .expect("Failed to execute command");
assert_eq!(String::from_utf8_lossy(&output.stdout), "Hello, world!\n");
// Nothing echoed to console

use std::process::{Command, Stdio};
let output = Command::new("echo")
    .arg("Hello, world!")
    .stdout(Stdio::inherit())
    .output()
    .expect("Failed to execute command");
// stdout 被打印到 console, 所以 Child.stdout 为空
assert_eq!(String::from_utf8_lossy(&output.stdout), "");
// "Hello, world!" echoed to console

use std::fs::File;
use std::process::Command;
let file = File::open("foo.txt").unwrap();
let reverse = Command::new("rev")
    .stdin(file)  // 从文件中读取输入
    .output()
    .expect("failed reverse command");
assert_eq!(reverse.stdout, b"!dlrow ,olleH");

use std::io::Write;
use std::process::{Command, Stdio};
let mut child = Command::new("rev")
    .stdin(Stdio::piped()) // 创建一个输入 pipe, Command 从它读取内容
    .stdout(Stdio::piped())
    .spawn()
    .expect("Failed to spawn child process");

// 父进程向 stdin 发送数据, 被 child 接收, child.stdin 是 Option<ChildStdin> 类型,
// 其中 ChildStdin 实现了 Write trait,child.stdin.take() 获得 ChildStdin 的所有权,
// 当它被 Drop 时关闭 stdin。
let mut stdin = child.stdin.take().expect("Failed to open stdin");
std::thread::spawn(move || {
    stdin.write_all("Hello, world!".as_bytes()).expect("Failed to write to stdin");
});
let output = child.wait_with_output().expect("Failed to read stdout");
assert_eq!(String::from_utf8_lossy(&output.stdout), "!dlrow ,olleH");

Stdio 可以从 std::io 的 File/Stderr/Stdout 和其它 Child 的 std::process::ChildStderr/ChildStdin/ChildStdout 来创建:

impl From<ChildStderr> for Stdio
impl From<ChildStdin> for Stdio
impl From<ChildStdout> for Stdio
impl From<File> for Stdio
impl From<Stderr> for Stdio // std::io::Stderr
impl From<Stdout> for Stdio // std::io::Stdout

// 将一个 Child Command 的 stdin/stdout/stderr 连接到其他 Child Command
use std::process::{Command, Stdio};
let hello = Command::new("echo")
    .arg("Hello, world!")
    // 必须 piped,后续才能从 Child 的 stdout field 获取到
    .stdout(Stdio::piped())
    .spawn()
    .expect("failed echo command");

let reverse = Command::new("rev")
    // Converted into a Stdio here。
    .stdin(hello.stdout.unwrap())
    .output()
    .expect("failed reverse command");
assert_eq!(reverse.stdout, b"!dlrow ,olleH\n");

// 将 Command 的输入输出连接到 File
use std::fs::File;
use std::process::Command;
// With the `foo.txt` file containing "Hello, world!"
let file = File::open("foo.txt").unwrap();
let reverse = Command::new("rev")
    .stdin(file)
    .output()
    .expect("failed reverse command");
assert_eq!(reverse.stdout, b"!dlrow ,olleH");


// 将 Command 的 stdout/stderr 连接到 std::io::Stdout/Stderr
#![feature(exit_status_error)]
use std::process::Command;
let output = Command::new("whoami")
    .stdout(std::io::stdout())
    .stderr(std::io::stderr())
    .output()?;
output.status.exit_ok()?;
assert!(output.stdout.is_empty());

15 std::io
#

std::io module 返回通用错误类型 std::io::Result<T> ,它是 std::io::Result<T, std::io::Error> 的别名。

std::io::Error 是实现了 std::error::Error trait 的 struct 类型,它的 kind() 方法返回 std::io::ErrorKind 枚举类型,可以用来进一步判断错误的原因:

use std::io::{Error, ErrorKind};

fn print_error(err: Error) {
    println!("{:?}", err.kind());
}

fn main() {
    print_error(Error::last_os_error());
    print_error(Error::new(ErrorKind::AddrInUse, "oh no!"));
}

std::io::prelude 包含了 std::io module 中常用的 trait 和类型,一般需要提前 use 导入。

Structs

BufReader
The BufReader<R> struct adds buffering to any reader.
BufWriter
Wraps a writer and buffers its output.
Bytes
An iterator over u8 values of a reader.
Chain
Adapter to chain together two readers.
Cursor
A Cursor wraps an in-memory buffer and provides it with a Seek implementation.
Empty
Empty ignores any data written via Write, and will always be empty (returning zero bytes) when read via Read .
Error
The error type for I/O operations of the Read, Write, Seek, and associated traits.
IntoInnerError
An error returned by BufWriter::into_inner which combines an error that happened while writing out the buffer, and the buffered writer object which may be used to recover from the condition.
IoSlice
A buffer type used with Write::write_vectored.
IoSliceMut
A buffer type used with Read::read_vectored.
LineWriter
Wraps a writer and buffers output to it, flushing whenever a newline (0x0a, ‘\n’) is detected.
Lines
An iterator over the lines of an instance of BufRead.
Repeat
A reader which yields one byte over and over and over and over and over and…
Sink
A writer which will move data into the void.
Split
An iterator over the contents of an instance of BufRead split on a particular byte.
Stderr
A handle to the standard error stream of a process.
StderrLock
A locked reference to the Stderr handle.
Stdin
A handle to the standard input stream of a process.
StdinLock
A locked reference to the Stdin handle.
Stdout
A handle to the global standard output stream of the current process.
StdoutLock
A locked reference to the Stdout handle.
Take
Reader adapter which limits the bytes read from an underlying reader.
WriterPanicked
Error returned for the buffered data from BufWriter::into_parts, when the underlying writer has previously panicked. Contains the (possibly partly written) buffered data.
BorrowedBuf
A borrowed byte buffer which is incrementally filled and initialized.
BorrowedCursor
A writeable view of the unfilled portion of a BorrowedBuf.

Traits:

  • BufRead A BufRead is a type of Reader which has an internal buffer, allowing it to perform extra ways of reading.
  • IsTerminal Trait to determine if a descriptor/handle refers to a terminal/tty.
  • Read The Read trait allows for reading bytes from a source.
  • Seek The Seek trait provides a cursor which can be moved within a stream of bytes.
  • Write A trait for objects which are byte-oriented sinks.

Functions

  • copy : Copies the entire contents of a reader into a writer.
  • empty : Creates a value that is always at EOF for reads, and ignores all data written.
  • read_to_string : Read all bytes from a reader into a new String.
  • repeat:Creates an instance of a reader that infinitely repeats one byte.
  • sink: Creates an instance of a writer which will successfully consume all data.
  • stderr: Constructs a new handle to the standard error of the current process.
  • stdin: Constructs a new handle to the standard input of the current process.
  • stdout: Constructs a new handle to the standard output of the current process.

Type Aliases

  • Result: A specialized Result type for I/O operations. std::io 自定义的 Result 别名类型
  • RawOsErrorExperimental The type of raw OS error codes returned by Error::raw_os_error.

15.1 Read
#

最多读取 buf.len() 的数据,所以传入的 buf 类型是 slice 而非动态大小的 Vec/String

  • fn read(&mut self, buf: &mut [u8]) -> Result<usize>;

read_to_end/read_to_string() 都是读取全部内容,大小未知,所以传入 Vec/String

  • fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> { … }
  • fn read_to_string(&mut self, buf: &mut String) -> Result<usize> { … }
  • bytes()
pub trait Read {
    // Required method
    fn read(&mut self, buf: &mut [u8]) -> Result<usize>;

    // Provided methods
    fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize>
    fn is_read_vectored(&self) -> bool { ... }

    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize>
    fn read_to_string(&mut self, buf: &mut String) -> Result<usize>

    fn read_exact(&mut self, buf: &mut [u8]) -> Result<()>
    fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()>
    fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<()>
    fn by_ref(&mut self) -> &mut Self where Self: Sized
    fn bytes(self) -> Bytes<Self> where Self: Sized { ... }
    fn chain<R: Read>(self, next: R) -> Chain<Self, R> where Self: Sized { ... }
    fn take(self, limit: u64) -> Take<Self> where Self: Sized { ... }
}

实现 Read 的类型:

impl Read for &File
impl Read for &TcpStream
// &Vec<T> 和 &[T; N] 都实现了 Read,&str.as_bytes 也返回 &[u8]
impl Read for &[u8]
impl Read for File
impl Read for TcpStream
impl Read for UnixStream

// std::process 下的 ChildStderr/ChildStdout
impl Read for ChildStderr
impl Read for ChildStdout

impl Read for Arc<File>
impl Read for Empty
impl Read for Repeat
impl Read for Stdin
impl Read for StdinLock<'_>

impl<'a> Read for &'a UnixStream
impl<A: Allocator> Read for VecDeque<u8, A>
impl<R: Read + ?Sized> Read for &mut R
impl<R: Read + ?Sized> Read for Box<R>
impl<R: ?Sized + Read> Read for BufReader<R>

// Cursor 也实现了 Read
impl<T> Read for Cursor<T> where T: AsRef<[u8]>,
impl<T: Read> Read for Take<T>
impl<T: Read, U: Read> Read for Chain<T, U>

例子:

use std::io;
use std::io::prelude::*;
use std::fs::File;

fn main() -> io::Result<()> {
    let mut f = File::open("foo.txt")?;
    let mut buffer = [0; 10]; // 指定 buf 大小为 10 bytes

    // read up to 10 bytes
    f.read(&mut buffer)?;

    let mut buffer = Vec::new();
    // read the whole file
    f.read_to_end(&mut buffer)?;

    // read into a String, so that you don't need to do the conversion.
    let mut buffer = String::new();
    f.read_to_string(&mut buffer)?;

    // and more! See the other methods for more details.
    Ok(())
}

15.2 BufRead
#

BufRead 是内部包含一个 buffer 的 Reader,它是 Read 的子类型,它提供了几个好用的方法:

  1. read_line(): 读取一行(包含行尾的换行)存入传入的 String buf;
  2. split(self): 返回一个迭代器,每次迭代 split 后的内容;
  3. lines(self): 返回一个迭代器,迭代返回 io::Result<String> ,字符串末尾不包含换行;
pub trait BufRead: Read {
    // Required methods
    fn fill_buf(&mut self) -> Result<&[u8]>;
    fn consume(&mut self, amt: usize);

    // Provided methods
    fn has_data_left(&mut self) -> Result<bool> { ... }
    fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> Result<usize> { ... }
    fn skip_until(&mut self, byte: u8) -> Result<usize> { ... }
    fn read_line(&mut self, buf: &mut String) -> Result<usize> { ... }
    fn split(self, byte: u8) -> Split<Self> where Self: Sized { ... }
    fn lines(self) -> Lines<Self> where Self: Sized { ... }
}

实现了 BufRead 的类型:&[u8]/StdinLock/BufReader/Cursor:

impl BufRead for &[u8]
impl BufRead for Empty
impl BufRead for StdinLock<'_>

impl<A: Allocator> BufRead for VecDeque<u8, A>
impl<B: BufRead + ?Sized> BufRead for &mut B
impl<B: BufRead + ?Sized> BufRead for Box<B>

impl<R: ?Sized + Read> BufRead for BufReader<R>

impl<T> BufRead for Cursor<T> where T: AsRef<[u8]>
impl<T: BufRead> BufRead for Take<T>
impl<T: BufRead, U: BufRead> BufRead for Chain<T, U>

15.3 Write
#

Write 是面向 byte 的输出:

  • write(): 写入 buf ,返回写出的字节数(所以一次 write 不一定写出了所有数据);
  • write_all():将 buf 内容写入到 object,确保全部都写入,否则返回 Err;
  • write_fmt(): 将传入的 Arguments(由 fmt!() 宏来生成)写入 object;
  • flush() : 确保 object 中 buff(如果有) 的数据写入到 true sink 中;
pub trait Write {
    // Required methods
    fn write(&mut self, buf: &[u8]) -> Result<usize>;
    fn flush(&mut self) -> Result<()>;

    // Provided methods
    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize>
    fn is_write_vectored(&self) -> bool
    fn write_all(&mut self, buf: &[u8]) -> Result<()>
    fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> Result<()>
    fn write_fmt(&mut self, fmt: Arguments<'_>) -> Result<()>
    fn by_ref(&mut self) -> &mut Self where Self: Sized
}

实现 Write 的类型:

impl Write for &File
impl Write for &TcpStream
impl Write for &ChildStdin
impl Write for &Empty
impl Write for &Sink
impl Write for &Stderr
impl Write for &Stdout
impl Write for &mut [u8]

impl Write for File
impl Write for TcpStream
impl Write for UnixStream
impl Write for ChildStdin
impl Write for Arc<File>
impl Write for Cursor<&mut [u8]>

impl Write for Empty
impl Write for Sink
impl Write for Stderr
impl Write for StderrLock<'_>
impl Write for Stdout
impl Write for StdoutLock<'_>

impl<'a> Write for &'a UnixStream
impl<'a> Write for BorrowedCursor<'a>
impl<A> Write for Cursor<&mut Vec<u8, A>> where A: Allocator,
impl<A> Write for Cursor<Box<[u8], A>> where A: Allocator,
impl<A> Write for Cursor<Vec<u8, A>> where A: Allocator,
impl<A: Allocator> Write for VecDeque<u8, A>
impl<A: Allocator> Write for Vec<u8, A>
impl<W: Write + ?Sized> Write for &mut W
impl<W: Write + ?Sized> Write for Box<W>
impl<W: ?Sized + Write> Write for BufWriter<W>
impl<W: ?Sized + Write> Write for LineWriter<W>
impl<const N: usize> Write for Cursor<[u8; N]>

示例:

use std::io::prelude::*;
use std::fs::File;

fn main() -> std::io::Result<()> {
    let data = b"some bytes";
    let mut pos = 0;
    let mut buffer = File::create("foo.txt")?;

    while pos < data.len() {
        let bytes_written = buffer.write(&data[pos..])?;
        pos += bytes_written;
    }
    Ok(())
}

use std::io::prelude::*;
use std::fs::File;

fn main() -> std::io::Result<()> {
    let mut buffer = File::create("foo.txt")?;

    // this call
    write!(buffer, "{:.*}", 2, 1.234567)?;
    // turns into this:
    buffer.write_fmt(format_args!("{:.*}", 2, 1.234567))?;
    Ok(())
}

15.4 Cursor
#

Cursor 是一个泛型类型:pub struct Cursor<T> { * private fields * }

它包装了一个内存 buffer 类型 T,提供 Seek、Read、BuffRead 或 Write 实现。

常见的内存 buffer 类型:

  • AsRef<[u8]>
  • Vec<u8>
  • &[u8]

Cursor 实现了 BufRead/Read/Seek/Write trait:

impl<T> BufRead for Cursor<T> where T: AsRef<[u8]>
impl<T> Read for Cursor<T> where T: AsRef<[u8]>
impl<T> Seek for Cursor<T> where T: AsRef<[u8]>

impl Write for Cursor<&mut [u8]>
impl<A> Write for Cursor<&mut Vec<u8, A>> where A: Allocator,
impl<const N: usize> Write for Cursor<[u8; N]>
impl<A> Write for Cursor<Box<[u8], A>> where A: Allocator,
impl<A> Write for Cursor<Vec<u8, A>> where A: Allocator,

例子:

use std::io::prelude::*;
use std::io::{self, SeekFrom};
use std::fs::File;

// a library function we've written
fn write_ten_bytes_at_end<W: Write + Seek>(mut writer: W) -> io::Result<()> {
    writer.seek(SeekFrom::End(-10))?;

    for i in 0..10 {
        writer.write(&[i])?;
    }

    // all went well
    Ok(())
}

// Here's some code that uses this library function.
//
// We might want to use a BufReader here for efficiency, but let's keep
// this example focused.
let mut file = File::create("foo.txt")?;

write_ten_bytes_at_end(&mut file)?;

// now let's write a test
#[test]
fn test_writes_bytes() {
    // setting up a real File is much slower than an in-memory buffer, let's
    // use a cursor instead
    use std::io::Cursor;
    let mut buff = Cursor::new(vec![0; 15]); // 从 Vec<i32> 创建 Cursor

    write_ten_bytes_at_end(&mut buff).unwrap(); // Cursor 实现了 Seek/Write

    assert_eq!(&buff.get_ref()[5..15], &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
}

15.5 Empty
#

使用 std::io:empty() 函数返回 Empty 对象。Empty 类型实现了 BufRead/Read/Write/Seek trait:

  • 读返回 EOF;
  • 写被丢弃;
use std::io::{self, Write};
let buffer = vec![1, 2, 3, 5, 8];
let num_bytes = io::empty().write(&buffer).unwrap();
assert_eq!(num_bytes, 5);

use std::io::{self, Read};
let mut buffer = String::new();
io::empty().read_to_string(&mut buffer).unwrap();
assert!(buffer.is_empty());

15.6 Repeat
#

调用 std::io::repeat(byte: u8) 函数产生一个 Repeat 对象, 它实现了 Read trait,一直返回该 byte:

use std::io::{self, Read};

let mut buffer = [0; 3];
io::repeat(0b101).read_exact(&mut buffer).unwrap();
assert_eq!(buffer, [0b101, 0b101, 0b101]);

15.7 Stdin/StdinLock/Stdout/StdoutLock/Stderr
#

std::io::stdin()/stdout()/stderr() 分别返回上面三种类型:

stdin() -> Stdin: Each handle returned is a reference to a shared global buffer whose access is synchronized via a mutex. If you need more explicit control over locking, see the Stdin::lock method.

  • Stdin 的方法自动获得 mutex lock 进行同步;
  • 也可以调用 Stdin::lock() 来获得 StdinLock 对象, 然后调用它的 read_line 方法或者它实现的 BufRead/Read trait 方法;
// Using implicit synchronization:
use std::io;
fn main() -> io::Result<()> {
    let mut buffer = String::new();
    io::stdin().read_line(&mut buffer)?;
    Ok(())
}

// Using explicit synchronization:
use std::io::{self, BufRead};
fn main() -> io::Result<()> {
    let mut buffer = String::new();
    let stdin = io::stdin();
    let mut handle = stdin.lock();
    handle.read_line(&mut buffer)?;
    Ok(())
}

// pub fn lines(self) -> Lines<StdinLock<'static>>
use std::io;
let lines = io::stdin().lines();
for line in lines {
    println!("got a line: {}", line.unwrap());
}

Stdout/StdoutLock 实现了 write trait, 其中 Stdout 的方法自动 mutex 同步, StdoutLock 不自动同步:

// Using implicit synchronization:
use std::io::{self, Write};
fn main() -> io::Result<()> {
    io::stdout().write_all(b"hello world")?;
    Ok(())
}

// Using explicit synchronization:
use std::io::{self, Write};
fn main() -> io::Result<()> {
    let stdout = io::stdout();
    let mut handle = stdout.lock();
    handle.write_all(b"hello world")?;
    Ok(())
}

// Using implicit synchronization:
use std::io::{self, Write};
fn main() -> io::Result<()> {
    io::stderr().write_all(b"hello world")?;

    Ok(())
}

// Using explicit synchronization:
use std::io::{self, Write};
fn main() -> io::Result<()> {
    let stderr = io::stderr();
    let mut handle = stderr.lock();

    handle.write_all(b"hello world")?;

    Ok(())
}

Sink 将丢弃写入的数据. All calls to write on the returned instance will return Ok(buf.len()) and the contents of the buffer will not be inspected.

use std::io::{self, Write};

let buffer = vec![1, 2, 3, 5, 8];
let num_bytes = io::sink().write(&buffer).unwrap();
assert_eq!(num_bytes, 5);

16 std::env
#

std::env module 提供了一些管理进程参数、环境变量、工作目录和临时目录的函数:

  1. args(): 获取进程命令行参数
  2. vars(): 获取进程环境变量列表
  3. var(): 获取进程特定环境变量
  4. remove_var/set_var(): 删除和设置环境变量
  5. temp_dir(): 返回临时目录
  6. current_dir/current_exec: 当前工作目录和二进制路径
  7. set_current_dir(): 设置进程当前工作目录;
  8. join_paths/split_paths(): 对 PATH 环境变量的路径进行处理;
use std::env;
use std::path::PathBuf;

fn main() -> Result<(), env::JoinPathsError> {
    if let Some(path) = env::var_os("PATH") {
        let mut paths = env::split_paths(&path).collect::<Vec<_>>();
        paths.push(PathBuf::from("/home/xyz/bin"));
        let new_path = env::join_paths(paths)?;
        env::set_var("PATH", &new_path);
    }
    Ok(())
}

pub fn args() -> Args, Args 是可迭代对象, 返回 String:

use std::env;
for argument in env::args() {
    println!("{argument}");
}

var/set_var/remove_var() 读取、设置和删除环境变量:

use std::env;

let key = "KEY";
env::set_var(key, "VALUE");
assert_eq!(env::var(key), Ok("VALUE".to_string()));

env::remove_var(key);
assert!(env::var(key).is_err());

match env::var(key) {
    Ok(val) => println!("{key}: {val:?}"),
    Err(e) => println!("couldn't interpret {key}: {e}"),
}

17 std::path
#

Structs

  • Ancestors An iterator over Path and its ancestors.
  • Components An iterator over the Components of a Path.
  • Iter An iterator over the Components of a Path, as OsStr slices.
  • Display Helper struct for safely printing paths with format! and {}.
  • Path A slice of a path (akin to str).
  • PathBuf An owned, mutable path (akin to String).
  • PrefixComponent A structure wrapping a Windows path prefix as well as its unparsed string representation.
  • StripPrefixError An error returned from Path::strip_prefix if the prefix was not found.

Path/PathBuf 用于实现 OS 无关的路径字符串, 它们是 OsString 和 OsStr 的瘦包装类型。

Path 是 unsized type,所以一般需要和 & 或 Box 使用。不可以改变,类似于 str。

PathBuf 也是 OS 无关的路径字符串,是 owned 版本,可以改变,类似于 String。

Struct std::path::Path

pub struct Path { /* private fields */ }

// 示例
use std::path::Path;
use std::ffi::OsStr;

// Note: this example does work on Windows
let path = Path::new("./foo/bar.txt"); // 传入的类型需要实现 AsRef<OsStr>
let string = String::from("foo.txt"); // &str,&String,&Path 都实现了 AsRef<OsStr>
let from_string = Path::new(&string);
let from_path = Path::new(&from_string);
assert_eq!(from_string, from_path);

let parent = path.parent();
assert_eq!(parent, Some(Path::new("./foo")));

let file_stem = path.file_stem();
assert_eq!(file_stem, Some(OsStr::new("bar")));

let extension = path.extension();
assert_eq!(extension, Some(OsStr::new("txt")));

Path 的方法:

  • pub fn new<S: AsRef<OsStr> + ?Sized>(s: &S) -> &Path
  • pub fn as_os_str(&self) -> &OsStr
  • pub fn as_mut_os_str(&mut self) -> &mut OsStr
  • pub fn to_str(&self) -> Option<&str>
  • pub fn to_path_buf(&self) -> PathBuf // 转换为 PathBuf
  • pub fn parent(&self) -> Option<&Path>
  • pub fn file_name(&self) -> Option<&OsStr>
  • pub fn strip_prefix<P>(&self, base: P) -> Result<&Path, StripPrefixError>
  • pub fn extension(&self) -> Option<&OsStr>
  • pub fn join<P: AsRef<Path>>(&self, path: P) -> PathBuf
  • pub fn with_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf

Path 和文件系统相关方法:

  • pub fn read_link(&self) -> Result<PathBuf>
  • pub fn read_dir(&self) -> Result<ReadDir>
  • pub fn exists(&self) -> bool
  • pub fn is_file(&self) -> bool
  • pub fn is_dir(&self) -> bool
  • pub fn is_symlink(&self) -> bool
  • pub fn metadata(&self) -> Result<Metadata>
  • pub fn symlink_metadata(&self) -> Result<Metadata>

&str/&String/&Path/&PathBuf/&OsStr/&OsString 均实现了 AsRef<OsStr>,都可以作为 Path::new() 的参数:

impl AsRef<OsStr> for Component<'_>
impl AsRef<OsStr> for str
impl AsRef<OsStr> for OsStr
impl AsRef<OsStr> for OsString
impl AsRef<OsStr> for Components<'_>
impl AsRef<OsStr> for std::path::Iter<'_>
impl AsRef<OsStr> for Path
impl AsRef<OsStr> for PathBuf
impl AsRef<OsStr> for String

impl AsRef<Path> for Cow<'_, OsStr>
impl AsRef<Path> for Component<'_>
impl AsRef<Path> for str
impl AsRef<Path> for OsStr
impl AsRef<Path> for OsString
impl AsRef<Path> for Components<'_>
impl AsRef<Path> for std::path::Iter<'_>
impl AsRef<Path> for Path
impl AsRef<Path> for PathBuf
impl AsRef<Path> for String

Struct std::path::PathBuf

// An owned, mutable path (akin to String).
pub struct PathBuf { /* private fields */ }

use std::path::PathBuf;
let mut path = PathBuf::new();
path.push(r"C:\");
path.push("windows");
path.push("system32");
path.set_extension("dll");

PathBuf 的方法:

  • pub fn new() -> PathBuf
  • pub fn with_capacity(capacity: usize) -> PathBuf
  • pub fn as_path(&self) -> &Path // 转换为 Path
  • pub fn push<P: AsRef<Path>>(&mut self, path: P)
  • pub fn set_file_name<S: AsRef<OsStr>>(&mut self, file_name: S)
  • pub fn set_extension<S: AsRef<OsStr>>(&mut self, extension: S) -> bool

PathBuf 实现了 Deref<Target=Path>,所以 PathBuf 可以使用 Path 的所有方法,并且 &PathBuf 可以当作 &Path 使用。

18 std::fs
#

文件和目录操作: std::fs 下泛型函数如果输入是 Path, 则是 AsRef<Path>, 所以实现了该 trait 的对象均可。

impl AsRef<Path> for Cow<'_, OsStr>
impl AsRef<Path> for Component<'_>
impl AsRef<Path> for str
impl AsRef<Path> for OsStr
impl AsRef<Path> for OsString
impl AsRef<Path> for Components<'_>
impl AsRef<Path> for std::path::Iter<'_>
impl AsRef<Path> for Path
impl AsRef<Path> for PathBuf
impl AsRef<Path> for String

Structs

  • DirBuilder A builder used to create directories in various manners.
  • DirEntry Entries returned by the ReadDir iterator.
  • File An object providing access to an open file on the filesystem.
  • FileTimes Representation of the various timestamps on a file.
  • FileType A structure representing a type of file with accessors for each file type. It is returned by Metadata::file_type method.
  • Metadata Metadata information about a file.
  • OpenOptions Options and flags which can be used to configure how a file is opened.
  • Permissions Representation of the various permissions on a file.
  • ReadDir Iterator over the entries in a directory.

Functions

  • canonicalize : Returns the canonical, absolute form of a path with all intermediate components normalized and symbolic links resolved.
  • copy : Copies the contents of one file to another. This function will also copy the permission bits of the original file to the destination file.
  • create_dir : Creates a new, empty directory at the provided path
  • create_dir_all : Recursively create a directory and all of its parent components if they are missing.
  • hard_link : Creates a new hard link on the filesystem.
  • metadata : Given a path, query the file system to get information about a file, directory, etc.
  • read : Read the entire contents of a file into a bytes vector.
  • read_dir : Returns an iterator over the entries within a directory.
  • read_link : Reads a symbolic link, returning the file that the link points to.
  • read_to_string : Read the entire contents of a file into a string.
  • remove_dir : Removes an empty directory.
  • remove_dir_all : Removes a directory at this path, after removing all its contents.
  • remove_file : Removes a file from the filesystem.
  • rename : Rename a file or directory to a new name, replacing the original file if to already exists.
  • set_permissions : Changes the permissions found on a file or a directory.
  • soft_linkDeprecated : Creates a new symbolic link on the filesystem.
  • symlink_metadata Query the metadata about a file without following symlinks.
  • write Write a slice as the entire contents of a file.
use std::fs;

fn main() -> Result<(), Box<dyn std::error::Error + 'static>> {
    let data: Vec<u8> = fs::read("image.jpg")?;
    assert_eq!(data[0..3], [0xFF, 0xD8, 0xFF]);
    Ok(())

    let message: String = fs::read_to_string("message.txt")?;
    println!("{}", message);
    Ok(())
}

use std::fs;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::prelude::*;
#[cfg(target_family = "unix")]
use std::os::unix;
#[cfg(target_family = "windows")]
use std::os::windows;
use std::path::Path;

// A simple implementation of `% cat path`
fn cat(path: &Path) -> io::Result<String> {
    let mut f = File::open(path)?;
    let mut s = String::new();
    match f.read_to_string(&mut s) { // 将文件的内容读取到字符串中
        Ok(_) => Ok(s),
        Err(e) => Err(e),
    }
}

// A simple implementation of `% echo s > path`
fn echo(s: &str, path: &Path) -> io::Result<()> {
    let mut f = File::create(path)?;
    f.write_all(s.as_bytes()) // 将 &[u8] 的内容一次性写入文件
}

// A simple implementation of `% touch path` (ignores existing files)
fn touch(path: &Path) -> io::Result<()> {
    match OpenOptions::new().create(true).write(true).open(path) {
        Ok(_) => Ok(()),
        Err(e) => Err(e),
    }
}

fn main() {
    println!("`mkdir a`");
    // Create a directory, returns `io::Result<()>`
    match fs::create_dir("a") {
        Err(why) => println!("! {:?}", why.kind()),
        Ok(_) => {},
    }

    println!("`echo hello > a/b.txt`");
    // The previous match can be simplified using the `unwrap_or_else` method
    echo("hello", &Path::new("a/b.txt")).unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`mkdir -p a/c/d`");
    // Recursively create a directory, returns `io::Result<()>`
    fs::create_dir_all("a/c/d").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`touch a/c/e.txt`");
    touch(&Path::new("a/c/e.txt")).unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`ln -s ../b.txt a/c/b.txt`");
    // Create a symbolic link, returns `io::Result<()>`
    #[cfg(target_family = "unix")] {
        unix::fs::symlink("../b.txt", "a/c/b.txt").unwrap_or_else(|why| {
            println!("! {:?}", why.kind());
        });
    }
    #[cfg(target_family = "windows")] {
        windows::fs::symlink_file("../b.txt", "a/c/b.txt").unwrap_or_else(|why| {
            println!("! {:?}", why.to_string());
        });
    }

    println!("`cat a/c/b.txt`");
    match cat(&Path::new("a/c/b.txt")) {
        Err(why) => println!("! {:?}", why.kind()),
        Ok(s) => println!("> {}", s),
    }

    println!("`ls a`");
    // Read the contents of a directory, returns `io::Result<Vec<Path>>`
    match fs::read_dir("a") {
        Err(why) => println!("! {:?}", why.kind()),
        Ok(paths) => for path in paths {
            println!("> {:?}", path.unwrap().path());
        },
    }

    println!("`rm a/c/e.txt`");
    // Remove a file, returns `io::Result<()>`
    fs::remove_file("a/c/e.txt").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`rmdir a/c/d`");
    // Remove an empty directory, returns `io::Result<()>`
    fs::remove_dir("a/c/d").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });
}

19 std::time
#

提供了如下类型:

Duration
代表时间间隔;
Instant
代表某一时刻, 可用于计算执行耗时;(一般用于记录进程时间)
SystemTime
代表某一个时刻, 和 Instant 不同的是,它不能确保是单调递增的(一般用于保存文件时间)。
let five_seconds = Duration::from_secs(5);
assert_eq!(five_seconds, Duration::from_millis(5_000));
assert_eq!(five_seconds, Duration::from_micros(5_000_000));
assert_eq!(five_seconds, Duration::from_nanos(5_000_000_000));

let ten_seconds = Duration::from_secs(10);
let seven_nanos = Duration::from_nanos(7);
let total = ten_seconds + seven_nanos; // Duration 实现了算术运算符重载
assert_eq!(total, Duration::new(10, 7)); // new() 参数是秒和纳秒

let now = Instant::now();
slow_function();
let elapsed_time = now.elapsed();
println!("Running slow_function() took {} seconds.", elapsed_time.as_secs());

Duration 代表时间跨度,由 second 和 nanoseconds 组成。实现了 Default/Add/Sub 等 std::ops 中运算符重载 trait。 Duration 默认实现了 Debug 而非 Display

use std::time::Duration;

// new() 参数是秒和纳秒
let five_seconds = Duration::new(5, 0);
let five_seconds_and_five_nanos = five_seconds + Duration::new(0, 5);

assert_eq!(five_seconds_and_five_nanos.as_secs(), 5);
// 只返回小数部分,单位是 nanos
assert_eq!(five_seconds_and_five_nanos.subsec_nanos(), 5);

let ten_millis = Duration::from_millis(10);

Duration 的方法或函数:

  • 实现了 Add/AddAssign/Sub/SubAssign/Div/DivAssign 等运算符,可以直接进行算术运算。
  • Instant.elapsed() 返回 Duration,而且方法参数也是 Duration;
// 创建
pub const fn new(secs: u64, nanos: u32) -> Duration
pub const fn from_secs(secs: u64) -> Duration
pub const fn from_millis(millis: u64) -> Duration
pub const fn from_micros(micros: u64) -> Duration
pub const fn from_nanos(nanos: u64) -> Duration

pub const fn is_zero(&self) -> bool

// 返回
pub const fn as_secs(&self) -> u64
pub const fn as_millis(&self) -> u128
pub const fn as_micros(&self) -> u128
pub const fn as_nanos(&self) -> u128

// 返回不足一秒的小数部分
pub const fn subsec_millis(&self) -> u32
pub const fn subsec_micros(&self) -> u32
pub const fn subsec_nanos(&self) -> u32

pub const fn abs_diff(self, other: Duration) -> Duration

pub const fn checked_add(self, rhs: Duration) -> Option<Duration>
pub const fn saturating_add(self, rhs: Duration) -> Duration
pub const fn checked_sub(self, rhs: Duration) -> Option<Duration>
pub const fn saturating_sub(self, rhs: Duration) -> Duration
pub const fn checked_mul(self, rhs: u32) -> Option<Duration>
pub const fn saturating_mul(self, rhs: u32) -> Duration
pub const fn checked_div(self, rhs: u32) -> Option<Duration>

pub fn as_secs_f64(&self) -> f64
pub fn as_secs_f32(&self) -> f32
pub fn from_secs_f64(secs: f64) -> Duration
pub fn from_secs_f32(secs: f32) -> Duration
pub fn mul_f64(self, rhs: f64) -> Duration
pub fn mul_f32(self, rhs: f32) -> Duration
pub fn div_f64(self, rhs: f64) -> Duration
pub fn div_f32(self, rhs: f32) -> Duration
pub fn div_duration_f64(self, rhs: Duration) -> f64
pub fn div_duration_f32(self, rhs: Duration) -> f32

impl Duration
pub fn try_from_secs_f32(secs: f32) -> Result<Duration, TryFromFloatSecsError>
pub fn try_from_secs_f64(secs: f64) -> Result<Duration, TryFromFloatSecsError>

Instant 代表某一个时刻,在程序中是单调递增的,故可以用来测量代码执行时间。

use std::time::{Duration, Instant};
use std::thread::sleep;

fn main() {
   let now = Instant::now();
   // we sleep for 2 seconds
   sleep(Duration::new(2, 0));
   // it prints '2'
   println!("{}", now.elapsed().as_secs());
}

Instant 的方法或函数:

impl Instant
pub fn now() -> Instant

// 两个 Instant 之间的差距
pub fn duration_since(&self, earlier: Instant) -> Duration
pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration>
pub fn saturating_duration_since(&self, earlier: Instant) -> Duration

// 时间流逝
pub fn elapsed(&self) -> Duration

// 添加 Duration 后的新 Instant
pub fn checked_add(&self, duration: Duration) -> Option<Instant>
pub fn checked_sub(&self, duration: Duration) -> Option<Instant>

20 std::sync
#

Arc 的 clone 返回共享一个单例对象的引用计数对象,所以只能获得该对象的共享借用,而不能获得可变借用,如果要修改 Arc 包含的对象,则需要使用 RefCell、Mutex 等具有内部可变性的类型。

20.1 mpsc
#

let (tx, rx) = mpsc::channel();

  1. tx 支持 clone() ,从而可以在多个线程中使用, 而 rx 不支持 clone(),所以只能有一个实例。如果要在多个线程中并发访问 rx,则需要 Arc + Mutext;
  2. tx clone 后的多个对象必须都被 drop 后, rx.recv() 才不会继续被 blocking;
  3. tx 发送的所有数据都串行缓冲, 即使 tx 都被 drop, 内容还在, 直到 rx 接受完数据;
  4. 数据发往 tx 后,所有权被转移;
let (tx, rx) = mpsc::channel();

let tx1 = tx.clone();
thread::spawn(move || {
    let vals = vec![
        String::from("hi"),
        String::from("from"),
        String::from("the"),
        String::from("thread"),
    ];

    for val in vals {
        tx1.send(val).unwrap(); // val 所有权被转移到 channel
        thread::sleep(Duration::from_secs(1));
    }
});

thread::spawn(move || {
    let vals = vec![
        String::from("more"),
        String::from("messages"),
        String::from("for"),
        String::from("you"),
    ];

    for val in vals {
        tx.send(val).unwrap();
        thread::sleep(Duration::from_secs(1));
    }
});

for received in rx { // 所有权转移给接收者。
    println!("Got: {}", received);
}

20.2 mutex
#

Mutex 可以作为全局 static 对象,用 Arc 包裹后可以在多线程环境中使用。

Mutex 支持内部可变性:mutex.lock().unwrap() 返回的是一个新 MutexGuard<’_, T> 对象, 所以可以给它赋值给 mut 类型变量(let mut data = data.lock().unwrap()), 进而可以对 MutexGuard 进行修改。

由于 MutexGuard 实现了 DerefMut<Target=T>, 所以可以像 &mut T 一样使用 data 变量.

use std::sync::{Mutex, Arc};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter = Arc::clone(&counter); // 惯例使用 Arc 类型的 clone 方法
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    // 这里 counter 前的 * 是可选的。
    println!("Result: {}", *counter.lock().unwrap());
}

20.3 RWLock
#

use std::sync::RwLock;

let lock = RwLock::new(5);

// many reader locks can be held at once
{
    // r1 和 r2 都是 std::sync::RwLockReadGuard 类型,它是实现了 Deref<Target=T> 的
    // 智能指针,故使用 * 操作来获得 T 的值(而非借用)。
    let r1 = lock.read().unwrap();
    let r2 = lock.read().unwrap();
    assert_eq!(*r1, 5);
    assert_eq!(*r2, 5);
} // read locks are dropped at this point

// only one write lock may be held, however
{
    let mut w = lock.write().unwrap();
    *w += 1;
    assert_eq!(*w, 6);
} // write lock is dropped here

20.4 condvar
#

Condvar 需要和 Mutex 一块使用:

use std::sync::{Arc, Mutex, Condvar};
use std::thread;

let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pair2 = Arc::clone(&pair);

thread::spawn(move|| {
    let (lock, cvar) = &*pair2;  // deref,lock 和 cvar 都是借用类型
    // started 是 MutexGuard 类型, 被 drop 时自动释放锁
    let mut started = lock.lock().unwrap();
    *started = true;
    cvar.notify_one();
});

// Wait for the thread to start up.
let (lock, cvar) = &*pair;
 // started 是 MutexGuard 类型
let mut started = lock.lock().unwrap();
// MutxtGuard 实现了 Deref, 所以 *started 返回内部的 bool 值
while !*started {
    // wait 需要传入 MutexGuard, 执行 wait() 时内部会使用 MutexGuard 释放锁,
    // 然后在收到 notify 时会再次获得锁
    started = cvar.wait(started).unwrap();
}

20.5 atomic
#

各种 atomic 类型如 AtomicBool, AtomicIsize, AtomicUsize, AtomicI8, AtomicU16 支持跨线程的原子更新, 主要实现无锁并发:

  • 各方法, 如 load/store/swap/fetch 等, 都是 &self, 不需要 &mut self;
  • 各 atomic 类型实现了 Sync 但没有实现 Send, 需要包裹在 Arc 中使用.
  • rust atomic follow C++20 atomics 规范, 每个方法都接受一个 Ordering 参数来指定 memory barrier 类型.
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::{hint, thread};

fn main() {
    let spinlock = Arc::new(AtomicUsize::new(1));

    let spinlock_clone = Arc::clone(&spinlock);
    let thread = thread::spawn(move|| {
        spinlock_clone.store(0, Ordering::SeqCst);
    });

    // Wait for the other thread to release the lock
    while spinlock.load(Ordering::SeqCst) != 0 {
        hint::spin_loop();
    }

    if let Err(panic) = thread.join() {
        println!("Thread had an error: {panic:?}");
    }
}

另外, atomic 支持内部可变性, 所以可以定义为全局变量, 后续可以原子修改. 也可以用 Arc 包裹后在多线程环境中使用。

// Keep a global count of live threads
use std::sync::atomic::{AtomicUsize, Ordering};

// 全局 static 变量, 即使不是 static mut 后续也可以更新(因为 atomic 的 fetch/load 的参数都是 &self
static GLOBAL_THREAD_COUNT: AtomicUsize = AtomicUsize::new(0);
let old_thread_count = GLOBAL_THREAD_COUNT.fetch_add(1, Ordering::SeqCst);
println!("live threads: {}", old_thread_count + 1);

通用方法:

pub fn compare_exchange(
    &self,
    current: bool,
    new: bool,
    success: Ordering,
    failure: Ordering,
) -> Result<bool, bool> {

pub fn compare_exchange_weak(
    &self,
    current: bool,
    new: bool,
    success: Ordering,
    failure: Ordering
) -> Result<bool, bool>

pub fn load(&self, order: Ordering) -> bool
pub fn store(&self, val: bool, order: Ordering)
pub fn swap(&self, val: bool, order: Ordering) -> bool

Ordering 类型:

pub enum Ordering {
    Relaxed,
    Release,
    Acquire,
    AcqRel,
    SeqCst,
}
  • Relaxed: 最宽松;
  • Release: store 写入操作时指定;
  • Acquire: load 读取时指定;
  • AcqRel: 同时 Acquire 和 Release, 在 load&store 时使用;
  • SeqCst: 最严格的模式;

20.6 Once/OnceLock/OnceCell
#

Once 是一个用于只进行一次全局初始化的类型,特别适用于 FFI 等场景:

  • 第一次调用 Once 类型值的 方法时,会执行对应闭包中的初始化逻辑,后续不会再执行该函数。
pub struct Once { /* private fields */ }

// 示例
use std::sync::Once;
static START: Once = Once::new();
START.call_once(|| {
    // run initialization here
});


// 另一个例子,用于设置全局变量 VAL
use std::sync::Once;
static mut VAL: usize = 0;
static INIT: Once = Once::new();

// Accessing a `static mut` is unsafe much of the time, but if we do so in a synchronized fashion
// (e.g., write once or read all) then we're good to go!
//
// This function will only call `expensive_computation` once, and will otherwise always return the
// value returned from the first invocation.
fn get_cached_val() -> usize {
    unsafe {
        INIT.call_once(|| {
            VAL = expensive_computation();
        });
        VAL
    }
}
fn expensive_computation() -> usize {
    // ...
}


// 初始化和查询
use std::sync::Once;
static INIT: Once = Once::new();
assert_eq!(INIT.is_completed(), false);
INIT.call_once(|| {
    assert_eq!(INIT.is_completed(), false);
});
assert_eq!(INIT.is_completed(), true);

Once 类型实现的方法:

impl Once
pub const fn new() -> Once

// Performs an initialization routine once and only once. The given closure will be executed if
// this is the first time call_once has been called, and otherwise the routine will not be
// invoked.
pub fn call_once<F>(&self, f: F) where F: FnOnce()

// Performs the same function as call_once() except ignores poisoning.
pub fn call_once_force<F>(&self, f: F) where F: FnOnce(&OnceState),

// Returns true if some call_once() call has completed successfully.
pub fn is_completed(&self) -> bool

Struct std::sync::OnceLock 是线程安全的 Struct std::cell::OnceCell 版本,用于封装 只能写入一次 的变量:

  • OnceLock 内部基于 Once 实现,只是 Once 使用 call_once () 初始化方法,而 OnceLock 提供了 get/get_or_init/set/take() 等更方便的方法。
  • OnceLock 实现了内部可变性 ,除了 get_mut() 方法是 &mut self 外,其他方法都是 &self 或 self, 而且 OnceLock 实现了 Send/Sync/Clone,所以可以 在多线程环境中无锁使用(不需要 move,也不需要加锁)

OnceLock 的方法:

  • 使用 set()/get_or_init()/get_or_try_init() 方法来设置一次 OnceLock。
  • static 常量具有 ‘static 生命周期,所以可以在多个线程里并发访问。(线程里不允许有非 ‘static 的引用,需要使用 move 闭包拥有所有权)
impl<T> OnceLock<T>
pub const fn new() -> OnceLock<T>

// Gets the reference to the underlying value.
// Returns None if the cell is empty, or being initialized. This method never blocks.
pub fn get(&self) -> Option<&T>

pub fn get_mut(&mut self) -> Option<&mut T>

// OnceLock 支持内部可变性,下面的方法都是 &self,所以可以同时在多个线程中无锁使用,也不需要 move。
pub fn set(&self, value: T) -> Result<(), T>
pub fn try_insert(&self, value: T) -> Result<&T, (&T, T)>
pub fn get_or_init<F>(&self, f: F) -> &T where F: FnOnce() -> T
pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E> where F: FnOnce() -> Result<T, E>

pub fn into_inner(self) -> Option<T>
pub fn take(&mut self) -> Option<T>

示例:

  1. 在函数内部定义 OnceLock 类型的 static 变量,实现 lazy static 或 memoizing ;
  2. 实现全局 static 常量;(因为只能写入或初始化一次,后续都是只读,所以不需要加锁。另外具有内部可变性,所以不需要是 static mut 静态变量。)
// 在函数内部定义 OnceLock 类型的 static 变量,实现 lazy static 或 memoizing
use std::sync::OnceLock;

struct DeepThought {
    answer: String,
}

impl DeepThought {
    fn new() -> Self {
        Self {
            // M3 Ultra takes about 16 million years in --release config
            answer: Self::great_question(),
        }
    }
}

fn computation() -> &'static DeepThought {
    // n.b. static items do not call [`Drop`] on program termination, so if
    // [`DeepThought`] impls Drop, that will not be used for this instance.

    // 静态常量(不是 static mut 静态变量),函数返回后还存在。
    static COMPUTATION: OnceLock<DeepThought> = OnceLock::new();
    // COMPUTATION 具有内部可变性,所以在共享借用情况下,调用 get_or_init() 方法,只会执行一次 init。
    COMPUTATION.get_or_init(|| DeepThought::new())
}

// The `DeepThought` is built, stored in the `OnceLock`, and returned.
let _ = computation().answer;

// The `DeepThought` is retrieved from the `OnceLock` and returned.
let _ = computation().answer;


// 另一个例子:在另一个线程中写入 OnceLock
use std::sync::OnceLock;

// 全局静态常量(具有内部可变性)
static CELL: OnceLock<usize> = OnceLock::new();

// `OnceLock` has not been written to yet.
assert!(CELL.get().is_none());

// Spawn a thread and write to `OnceLock`.
std::thread::spawn(|| {
    // 由于 CELL.get_or_init(&self, f: F) 是 &self,所以不需要 move
    let value = CELL.get_or_init(|| 12345);
    assert_eq!(value, &12345);
})
    .join()
    .unwrap();

// `OnceLock` now contains the value.
assert_eq!(
    CELL.get(),
    Some(&12345),
);

20.7 LazyLock/LazyCell
#

Rust 1.80 开始支持 LazyLock, 广泛使用的 lazy_static 可以使用 std::sync::LazyLock 来代替。

相比 OnceLock, 更建议使用 LazyLock<T, F>,因为 LazyLock 在首次 Deref<Target=T> 自动调用 F 闭包 进行初始化,而 OnceLock 需要显式的调用 get_or_init() 方法来进行初始化。

std::sync::LazyLockstd::cell::LazyCell 的线程安全版本,内部封装了 Once 和 UnsafeCell, 也具有内部可变性 ,所以可以用于定义 static 常量。

  • static 常量具有 ‘static 生命周期,所以可以在多个线程里并发访问。(线程里不允许有非 ‘static 的引用,需要使用 move 闭包获得所有权)
use std::sync::LazyLock;

// 创建 LazyLock 时指定初始化闭包。
// DEEP_THOUGHT 具有内部可变性,不需要定义为 static mut 类型。
static DEEP_THOUGHT: LazyLock<String> = LazyLock::new(|| {
    another_crate::great_question()
});
// 第一个 Deref 时自动调用初始化闭包。
let _ = &*DEEP_THOUGHT;
// 后续返回已初始化的值。
let _ = &*DEEP_THOUGHT;


// Lazyock 用在 struct field 中
use std::sync::LazyLock;

#[derive(Debug)]
struct UseCellLock {
    number: LazyLock<u32>,
}
fn main() {
    let lock: LazyLock<u32> = LazyLock::new(|| 0u32);

    let data = UseCellLock { number: lock }; // 此时 number field 未被初始化
    println!("{}", *data.number); // number field 被初始化
}

21 std::thread
#

并发编程(concurrent programming)与并行编程(parallel programming)这两种概念随着计算机设备的多核心化而变得越来越重要。前者允许程序中的不同部分相互独立地运行,而后者则允许程序中的不同部分同时执行。

由于绿色线程的 M:N 模型需要一个较大的运行时来管理线程,所以 Rust 标准库只提供了 1:1 线程模型的实现。

Rust thread 使用 thread::spawn() 来运行一个 thread,传入的闭包函数必须满足 Send + ‘static。

pub fn spawn<F, T>(f: F) -> JoinHandle<T>
where
    F: FnOnce() -> T + Send + 'static,
    T: Send + 'static,

由于该闭包函数 F 在某个 thread 中运行,thread closure 不能使用外围对象的共享引用 。因为编译器不能确定该 thread 和外围 thread 的生命周期关系,如果外围 thread 退出导致对象被 drop,则该 thread 的引用将失效。所以,一般使用 move 来将引用对象的所有权转移到闭包中, 从而满足 ‘static 定义。

例外情况是,闭包中只引用 static 静态(全局)常量,由于全局常量具有 ‘static 生命周期,这时不需要 move 转移所有权。常见的使用场景是跨进程访问的,具有内部可变性的全局静态变量 static GLOBAL_VAR = Arc<Mutex<T>>;

// 错误
use std::thread;
fn main() {
    let v = vec![1, 2, 3];

    let handle = thread::spawn(|| {
        // 编译失败,没有加 move 时默认是 reference
        println!("Here's a vector: {:?}", v);
    });

    handle.join().unwrap();
}

// 正确
fn main() {
    let v = vec![1, 2, 3];
    // move 指示 closure 捕获的外部对象,都是 ownership 接管
    let handle = thread::spawn(move || {
        // 而非引用的方式
        println!("Here's a vector: {:?}", v);
    });
    // 错误,因为 v ownership 已经转义到线程闭包函数中,故这里不能再引用它。
    // drop(v);
    handle.join().unwrap();
}

通过在线程闭包函数中 move 接管外部对象,Rust 的 ownership 规则可以防止并发编程错误(编译时报错)。

如果只能有一个变量拥有对象所有权,当要在多个线程中共享同一个对象时,可以使用线程安全的 Arc<T> 智能指针,通过 clone 创建的 Arc<T> 都是 T 的共享引用,不能修改(但可以使用内部可变性对象)。

let value = Arc::new(myType);

for _ in 0..5 {
    // 每创建一个 thread 前,先 clone Arc,增加 value 的引用计数
    let value2 = Arc::clone(&value); // 或者 let value2 = value.clone();

    // move closure 函数捕获 value2 ownership
    thread.spawn(move || {
        let f = value2.filed;
        // ...
        // 线程推出时,value2 被 drop,引用计数减少
    })
}

由于 Mutext<T>/RWlock<T>/AutomicXX<T> 都是线程安全的 内部可变性对象 ,所以通过 Arc<Mutex<T>> Deref 生成的 mutex 共享引用,然后调用 lock() 方法来修改。

闭包的返回值可以通过 JoinHandler.join() 来获取, 它返回的是 std::thread::Result 类型, 如果子线程 panic 则对应 Err, 否则 Ok(value) ,value 值为 spawn() 闭包函数的返回值。

可以使用 builder 模式来自定义 thread name/stack_size 参数(默认是 2MiB,也可以通过 RUST_MIN_STACK 环境变量来设置); thread name 是可选的。

use std::thread;

let thread_join_handle = thread::spawn(move || {
    // some work here
});

// some work here
let res = thread_join_handle.join().unwrap();


// 配置 thread, 如 name/stack_size
use std::thread;
let handler = thread::Builder::new().name("thread1".to_string()).spawn(
  move || {
    println!("Hello, world!");
  }).unwrap();
handler.join().unwrap();

Thread 对象的方法:id()/name()/unpark()

  • id() 返回的 std::thread::ThreadId 可以通过 as_u64() 返回编号;
  • 每个线程都有一个无锁的 blocking 机制,使用 std::thread::park()/park_timeout() 来暂停,然后使用 Thread 对象的 unpark()方法来继续。如果在线程运行前,已经被 unpark(), 则线程运行到 thread::park() 时会直接返回。
use std::thread;
use std::time::Duration;

let parked_thread = thread::Builder::new()
    .spawn(|| {
        println!("Parking thread");
        thread::park(); // 暂停
        println!("Thread unparked");
    })
    .unwrap();

// Let some time pass for the thread to be spawned.
thread::sleep(Duration::from_millis(10));

println!("Unpark the thread");
parked_thread.thread().unpark(); // 继续
parked_thread.join().unwrap();


// 另一例子,使用 park/unpark 来进行初始化同步。
use std::thread;
use std::sync::{Arc, atomic::{Ordering, AtomicBool}};
use std::time::Duration;

let flag = Arc::new(AtomicBool::new(false));
let flag2 = Arc::clone(&flag);

let parked_thread = thread::spawn(move || {
    // We want to wait until the flag is set. We *could* just spin,
    // but using park/unpark is more efficient.
    while !flag2.load(Ordering::Relaxed) {
        println!("Parking thread");
        thread::park();
        // We *could* get here spuriously, i.e., way before the 10ms
        // below are over! But that is no problem, we are in a loop until the
        // flag is set anyway.
        println!("Thread unparked");
    }
    println!("Flag received");
});

// Let some time pass for the thread to be spawned.
thread::sleep(Duration::from_millis(10));

// Set the flag, and let the thread wake up.
// There is no race condition here, if `unpark`
// happens first, `park` will return immediately.
// Hence there is no risk of a deadlock.
flag.store(true, Ordering::Relaxed);
println!("Unpark the thread");
parked_thread.thread().unpark();

parked_thread.join().unwrap();

21.1 scope thread
#

thread closure 的签名是 dyn FnOnce() -> T + Send + 'static ,thread 因为不能确保在主函数返回前 thread 一定执行完毕和被 join, 所以不能使用父线程 stack 上的变量引用(不满足 ‘static 要求):

use std::thread;

let people = vec![
    "Alice".to_string(),
    "Bob".to_string(),
    "Carol".to_string(),
];

let mut threads = Vec::new();

for person in &people {
    threads.push(thread::spawn(move || {
        println!("Hello, {}!", person);
    }));
}

for thread in threads {
    thread.join().unwrap();
}

报错:

error[E0597]: `people` does not live long enough
  --> src/main.rs:12:20
   |
12 |     for person in &people {
   |                    ^^^^^^ borrowed value does not live long enough
...
21 | }
   | - borrowed value only lives until here
   |
   = note: borrowed value must be valid for the static lifetime...

解决办法: 使用 scope thread , 它确保在 scope 函数返回前, 内部 spawn 的 thread 都被 join(显式或自动), 所以可以不使用 move 的情况下引用 stack 变量:

  • std::thread::scope() 参数是 Scope 类型闭包,使用 scope.spawn() 来创建线程;
  • scope() 函数返回前自动 join 所有内部 spawn 的线程;
  • Scope.spawn() 的参数是线程闭包函数 FnOnce(), 内部可以共享引用 stack 变量, 也可以 &mut 引用 stack 变量, 但是要确保只能有一个 spawn() 闭包是 mut 使用该变量;
pub fn scope<'env, F, T>(f: F) -> T
where
    // 没有 Send + 'static 的要求
    F: for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> T,

// 示例
use std::thread;

let mut a = vec![1, 2, 3];
let mut x = 0;

thread::scope(|s| { // s 是一个 &std::thread::Scope 类型对象
    s.spawn(|| {  // 使用 Scope.spawn() 来创建闭包
        println!("hello from the first scoped thread");
        dbg!(&a); // 共享借用 a
    });
    s.spawn(|| {
        println!("hello from the second scoped thread");
        x += a[0] + a[2]; // 共享借用 a ,可变借用 x
    });
    println!("hello from the main thread");
}); // thread::scope() 确保内部 spawn 的线程都执行返回后,再返回。

a.push(4);
assert_eq!(x, a.len());

let mut data = vec![5];
std::thread::scope(|s| {
    for _ in 0..5 {
        s.spawn(|| println!("{:?}", data));
    }
});

21.2 thread local storage
#

使用 thread_local!() 宏来创建 static LocalKey<T> 类型全局常量(具有内部可变性)。

LocalKey<T> 提供了 get/set/with() 等方法,参数都是 &self, 所以为了修改值, LocalKey 的泛型参数类型 T 需要实现内部可变性 ,一般使用 Cell/RefCell 来包裹(因为是线程本地存储,所以不会有并发问题)。

  • get() 返回 T,T 需要实现 Copy。
  • take() 返回 T,T 需要实现 Default。
  • Rust 为 LocalKey<RefCell<T>> 提供了特殊支持:with_borrow() 和 with_borrow_mut()
use std::cell::RefCell;
use std::thread;

// FOO 类型需要支持内部可变性, 所以使用 Cell/RefCell
thread_local!(static FOO: RefCell<u32> = RefCell::new(1));

FOO.with_borrow(|v| assert_eq!(*v, 1));
FOO.with_borrow_mut(|v| *v = 2);

// 每个线程都有自己的 FOO 对象

let t = thread::spawn(move|| {
    // 子线程的 FOO 对象
    FOO.with_borrow(|v| assert_eq!(*v, 1));
    FOO.with_borrow_mut(|v| *v = 3);
});

t.join().unwrap();

// 主线程的 FOO 对象
FOO.with_borrow(|v| assert_eq!(*v, 2));

22 std::net
#

std::net::ToSocketAddrs trait 用于实现域名解析,它的 to_socket_addrs() 方法返回解析后的 IP:

use std::net::{SocketAddr, ToSocketAddrs};
// assuming 'localhost' resolves to 127.0.0.1
let mut addrs_iter = "localhost:443".to_socket_addrs().unwrap();
assert_eq!(addrs_iter.next(), Some(SocketAddr::from(([127, 0, 0, 1], 443))));
rust-crate - 这篇文章属于一个选集。

相关文章

tokio
··25105 字
Rust Rust-Crate
Tokio 是 Rust 主流的异步运行时库。它提供了异步编程所需要的所有内容:单线程或多线程的异步任务运行时、工作窃取、异步网络/文件/进程/同步等 APIs。
anyhow
··1872 字
Rust Rust-Crate
anyhow crate 提供了自定义 Error 类型和 Result 类型,Error 类型自带 backtrace 和 context,支持用户友好的格式化信息输出。
bytes
··2900 字
Rust Rust-Crate
bytes 提供了高效的 zero-copy 连续内存区域的共享和读写能力。
chrono
··4003 字
Rust Rust-Crate
chrono 提供了丰富的 Date/Time 类型和相关操作。