Rust—安全数据访问与数据转换方法


Rust标准库提供了丰富的方法来安全地访问或转换数据的引用


借用方法

.borrow()

borrow() 方法通常用于 RefCell<T> 类型,但也可以用于其他实现了 DerefBorrow trait 的类型。它返回一个不可变引用 &T,用于在运行时动态检查借用规则。

  • 用途:适用于需要在运行时动态检查借用规则的情况,尤其是在单线程环境中实现内部可变性时。
  • 生命周期:使用 .borrow() 获取的引用生命周期与调用者的作用域相同。
  • 当尝试获取可变借用时,如果有现存的不可变或可变借用,RefCell会panic。
use std::cell::RefCell;
let data = RefCell::new(5);
let borrow = data.borrow();
// let mut_borrow = data.borrow_mut(); // 这会panic

.borrow_mut()

borrow_mut() 方法主要用于 RefCell<T>,但也可以用于其他实现了 DerefMutBorrowMut trait 的类型。它用于获取可变引用 &mut T,并在运行时检查借用规则,如果有任何现存的借用(不可变或可变),都会导致 panic。

  • 用途:提供在单线程环境中对数据进行可变访问的能力,同时保持内存安全性。
  • 运行时检查借用规则,如果有任何现存的借用(不论是不可变还是可变),都会导致panic。
  • 提供了在单线程环境中对数据进行可变访问的能力,同时保持内存安全性。
use std::cell::RefCell;
let data = RefCell::new(5);
let mut borrow_mut = data.borrow_mut();
// let borrow = data.borrow(); // 这会panic

引用转换方法

.as_ref()

适用于 Option<T>, Box<T>, Vec<T> 等类型,用来获取内部数据的不可变引用 &T,而不改变所有权。如果容器内部有值,则返回内部值的引用;如果为 None,则返回 None

  • 用途:简化对内部数据的只读访问。
  • 扩展性:自定义类型可以通过实现 AsRef<T> trait 来提供 .as_ref() 方法。
let opt = Some(5);
if let Some(ref value) = opt.as_ref() { 
   println!("{}", value);
}

.as_mut()

类似于 .as_ref(),但用于获取可变引用 &mut T。适用于 Option<T>, Box<T> 等,当需要可变访问内部数据而不转移所有权时使用。

  • 用途:允许可变地访问容器内部的数据。
let mut opt = Some(5);
if let Some(ref mut value) = opt.as_mut() { 
   *value = 10;
}

集合访问方法

.get() 和 .get_mut()

通常用于访问 Vec, HashMap, BTreeMap 等集合类型中的元素。这两个方法提供了安全访问集合内元素的方式,尤其是在索引可能越界的情况下,相比直接使用索引操作符 [ ] 更加安全。由于需要进行边界检查,会有一定的性能开销。

  • .get(index):返回指定索引位置的元素的不可变引用,如果索引越界则返回 None
  • .get_mut(index):返回可变引用,如果索引越界则返回 None
let vec = vec![1, 2, 3];
if let Some(value) = vec.get(1) {
    println!("{}", value);
}

原始指针方法

.as_ptr() 和 .as_mut_ptr()

用于获取原始指针(raw pointers)到容器的数据。

  • .as_ptr():返回不可变指针。
  • .as_mut_ptr():返回可变指针。
  • 用途:主要用于底层操作或与 C 库交互,使用时需特别小心,因为它们绕过了 Rust 的安全性检查。
let vec = vec![1, 2, 3];
let ptr = vec.as_ptr();

迭代器方法

.into_iter(), .iter(), .iter_mut()

用于迭代容器中的元素。适用于Vec, HashMap, HashSet, VecDeque等多种集合类型

  • .into_iter():消费容器并提供元素的所有权。
  • .iter():提供不可变引用的迭代器。
  • .iter_mut():提供可变引用的迭代器。
  • 注意这些方法返回的迭代器具有不同的特性,.into_iter()消费容器本身,而.iter().iter_mut()则保留容器。
let vec = vec![1, 2, 3];
for val in vec.iter() {
    println!("{}", val);
}

其他方法

.as_deref() 和 .as_deref_mut()

适用于 Option<Box<T>>Option<&T> 等类型,便捷地解引用获取内部的引用。

  • .as_deref():返回 Option<&T>
  • .as_deref_mut():返回 Option<&mut T>
  • 这两个方法是Rust 1.46引入的,用于从Option<Box<T>>或Option<&T>这样的类型中便捷地解引用获取内部的引用。
let opt = Some(Box::new(5));
if let Some(value) = opt.as_deref() {
    println!("{}", value);
}

.map(), .map_or, .map_or_else, 和 .map_mut()

虽然不是直接获取引用的方法,但在处理 OptionResult 时非常有用。

  • .map():用于不可变地映射内部值。
  • .map_or():如果 OptionNone,返回默认值,否则映射内部值。
  • .map_or_else():如果 OptionNone,调用提供的闭包并返回结果,否则映射内部值。
  • .map_mut():用于可变地映射内部值。
  • 不是直接获取引用的方法,但(特别是.map())在处理Option或Result时非常有用,可以用来转换或操作内部值的同时保持原类型。
  • .map_mut()是第三方库如OptionExt中提供的,用于可变地映射Option<&mut T>。
let opt = Some(5);
let new_opt = opt.map(|x| x * 2);

let default = opt.map_or(0, |x| x * 2);

let computed_default = opt.map_or_else(|| 0, |x| x * 2);

.as_slice() 和 .as_mut_slice()

适用于 Vec<T> 等集合,将其视为 &[T](不可变切片)或 &mut [T](可变切片)来访问。

  • 对于像Vec<T>这样的集合,可以使用.as_slice()将其视为 &[T](不可变切片)来访问,或使用.as_mut_slice()视为&mut [T](可变切片)来访问
  • as_slice() 和 .as_mut_slice() 允许以切片形式高效访问集合内容,对于需要连续内存访问或传递给期望切片参数的函数时非常有用。
let vec = vec![1, 2, 3];
let slice: &[i32] = vec.as_slice();

.drain()

适用于 Vec 等集合,创建一个移除并迭代集合中元素的迭代器,同时修改原集合。

  • 适用于Vec等集合,它创建一个移除并迭代集合中元素的迭代器,同时修改原集合。这提供了一种移除多个元素同时处理它们的方法,而不仅仅是借用查看。
  • 除了移除元素外,.drain()还允许你在移除过程中直接操作或过滤这些元素,是一种高效处理集合子集的手段
let mut vec = vec![1, 2, 3];
for elem in vec.drain(..) {
    println!("{}", elem);
}

.split_at_mut()

用于可变引用 &mut [T],将其分割成两个独立的可变引用。

  • 对于可变引用&mut [T],此方法分割成两个独立的可变引用,使得可以同时安全地操作原切片的两部分。这对于需要同时处理数据的不同部分很有用。
  • split_at_mut()在并行处理数据时特别有用,因为它保证了两个产生的可变引用不会重叠,从而符合Rust的内存安全规
let mut slice = &mut [1, 2, 3, 4];
let (left, right) = slice.split_at_mut(2);

.zip() 和 .enumerate()

虽然不是直接的引用获取方法,但在处理集合时非常有用。

.zip()可以将两个迭代器组合成一个,同时提供对两个集合元素的引用。

enumerate()为迭代器的每个元素附加一个索引,返回(usize, &T)或(usize, &mut T)。

let vec1 = vec![1, 2, 3];
let vec2 = vec![4, 5, 6];
for (a, b) in vec1.iter().zip(vec2.iter()) {
    println!("{}, {}", a, b);
}

.try_into() 和 .try_from()

用于类型转换的trait方法,特别是当转换可能失败时(例如大小不匹配),它们提供了比直接转换更安全的选项,并且可以返回Result类型来处理错误。

use std::convert::TryInto;
let num: i32 = 5;
let result: Result<u8, _> = num.try_into();

.unwrap_or_default()

简化 Option 处理,对于 Option<T> 当值为 None 时,使用 Default trait 来提供一个默认值。

let opt: Option<i32> = None;
let value = opt.unwrap_or_default();