1 std::alloc #
标准库使用一个 global 内存分配器来为 Box<T>/Vec<T>
等分配堆内存:
- 默认情况下,使用
std::alloc::System
作为 global,System同时实现
了 Allocator 和 GlobalAlloc trait。- unix/linux:使用 malloc 实现。
- windows:使用 HeapAlloc 实现。
- 用户可以使用
#[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 反射):
type_name::<T>()
: 返回 T 的类型名称字符串;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
值才会捕获调用栈,否则是关闭的:
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.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>
的智能指针,自动解引用和类型转换:
- 支持 * 运算符来解引用,
*v
等效于*(v.deref())
,返回T 值
, &*v 来返回&T 值
- 在需要 &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:
- Cell::new(value):转移 value 对象的所有权;
- cell.get():使用对象的 Copy trait 来返回 value;
- 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>:
- RefCell::new(value) : 转移 value 的所有权;
- ref_cell.borrow():返回对象的共享借用类型
Ref<T>
, 如果该对象已经被&mut
则运行时 panic; - ref_cell.borrow_mut() :返回对象的可变借用类型
RefMut<T>
,如果对象已经被共享借用则运行时 panic; - 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 到新的连续内存块, 所以:
- 有性能开销, 涉及到内存数据的复制移动;
- 会导致已有的 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>
,共享了很多相同的方法,
不同之处在于它们组织条目的方式。
-
HashMap: 把键和值都存储在哈希表中,因此它要求键的类型 K 实现了
Hash 和 Eq
:- bool、int、uint、String、&str 等;
- float 没有实现这两个 trait,不能用于 key;
- 对于 collection,如果它们的元素类型都实现了 Hash 和 Eq,则 collection 也实现了 Hash 和 Eq,例如 Vec<u8>;
-
BTreeMap: 按照键的顺序在
树形结构中
存储条目,键的类型 K 需要实现 Ord。
总结:
- HashMap 是使用
一块连续的
heap 来保存 map 元素的, 如果 map 容量要增加, 则需要分配一块新的连续内存区域, 并将老的 map 元素移动过去,有一定性能开销; - 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:
- 以值迭代:for (k, v) in map,产生(K, V)对, 这会消耗掉 map。
- 迭代共享引用:for (k, v) in &map,产生(&K, &V)对。
- 迭代可变引用:for (k, v) in &mut map,产生(&K, &mut V)对。
- map.keys(): 返回一个只迭代键的迭代器,以引用的形式返回。
- map.values():返回一个只迭代值的迭代器,以引用的形式返回。
- 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 的方法:
- 以值迭代(for v in set)产生set的成员(并消耗这个 set)。
- 以共享引用迭代(for v in &set)产生set中成员的共享引用。
不支持以 mut 引用迭代 set。也没有方法获取 set 中值的 mut 引用。
set.iter() 返回一个以共享引用方式迭代 set 的迭代器。
HashSet 迭代器类似于 HashMap 的迭代器,也会以任意顺序产生值。
BTreeSet 迭代器按照顺序产生值,类似于一个排序过的 Vec。
set 的一些方法:
- set.get(&value): 返回 value 的共享引用,类型是 Option<&T>;
- set.take(&value): 与 set.remove(&value) 类似,返回移除的值,类型是 Option<&T>;
- set.replace(value);
两个 set 间的方法:
- set1.intersection(&set2) : 返回同时出现在 set1 和 set2 中的值的迭代器;
- &set1 & &set2: 返回一个新 set,是 set1 和 set2 的交集;
- set1.union(&set2): 返回并集迭代器;等效于&set1 | &set2
- set1.difference(&set2): 返回差集迭代器; 等效于 &set1 - &set2
- 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:
- 如果是 main thread panic,则程序退出;
- 否则,如果是 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 提供了两种错误处理类型:
- panic runtime 和接口;
- 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 的类型:
- 必须实现 Display 和 Debug trait;
- 可以重新定义
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");
格式化:
字符串默认是左对齐,数字是右对齐
;- 位置参数和命名参数可以混用,但命名参数必须位于位置参数后面;
- 命名参数的名称也可以是上下文中的变量名称;
语法:
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;
- bool = 1;
- char = 4;
- *const T, &T, Box<T>, Option<&T>, 和 Option<Box<T>> 具有相同大小, 如果 T 是 Sized, 则这些类型大小和 usize 一致;
- [T; n]: 大小为
n * size_of::<T>()
- Enums: 如果 Enum 的各 variant 除了 tag 外都没有 data,则大小和 C enum 一致;
- 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)。
两个常用场景:
- 在 *const 指针和函数指针间转换;
扩充或缩短 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) -> Dst
: Interprets 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 和引用都是指针,包括两部分:
- data pointer:指向 value 内存地址的指针;
- 可选的 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 显式转换):
- &mut T to &T <- 可变引用可以转换为不可变引用
- *mut T to *const T <– 可变 raw pointer 可以转换为不可变 raw pointer
- &T to *const T <– 不可变引用 可以转换为 不可变 raw pointer
- &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 srcunchanged.
- 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() 函数:
- 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,会出现
多重释放的问题
。
- 如果 T 没有实现 Copy,则 src 和返回的 T 由于指向同一个内存区域,在返回的 T 对象被
drop 后,后续如果再释放 src,会出现
- 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() 的注意点:
- 不会 drop dst,有可能导致 dst 指向的内存区域内存泄露;
- 不会 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, 默认的行为如下:
- No arguments to the program
- Inherit the current process’s
environment
- Inherit the current process’s
working directory
- Inherit stdin/stdout/stderr for
spawn() or status()
, but create pipes foroutput()
- 对于 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 viaRead
. - 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 的子类型,它提供了几个好用的方法:
- read_line(): 读取一行(包含行尾的换行)存入传入的 String buf;
- split(self): 返回一个迭代器,每次迭代 split 后的内容;
- 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 提供了一些管理进程参数、环境变量、工作目录和临时目录的函数:
- args(): 获取进程命令行参数
- vars(): 获取进程环境变量列表
- var(): 获取进程特定环境变量
- remove_var/set_var(): 删除和设置环境变量
- temp_dir(): 返回临时目录
- current_dir/current_exec: 当前工作目录和二进制路径
- set_current_dir(): 设置进程当前工作目录;
- 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 thepermission 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 intoa 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 fileinto 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();
- tx 支持 clone() ,从而可以在多个线程中使用, 而 rx 不支持 clone(),所以只能有一个实例。如果要在多个线程中并发访问 rx,则需要 Arc + Mutext;
- tx clone 后的多个对象必须都被 drop 后, rx.recv() 才不会继续被 blocking;
- tx 发送的所有数据都串行缓冲, 即使 tx 都被 drop, 内容还在, 直到 rx 接受完数据;
- 数据发往 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>
示例:
- 在函数内部定义 OnceLock 类型的 static 变量,实现 lazy static 或 memoizing ;
- 实现全局 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::LazyLock
是 std::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))));