Skip to content

Ch 5: std::borrow - 借用与所有权

std::borrow 模块提供了 Rust 中关于借用的核心 trait 和类型。理解这些抽象对于编写灵活的 API、设计库代码、以及深入理解 Rust 的所有权系统至关重要。本章将讲解 Borrow、BorrowMut 和 Cow 三个核心概念。

Borrow trait 允许一个类型借用为另一个类型的引用:

trait 定义:

pub trait Borrow<Borrowed> {
fn borrow(&self) -> &Borrowed;
}

实现示例:

use std::borrow::Borrow;
#[derive(Debug)]
struct StringContainer(String);
impl Borrow<str> for StringContainer {
fn borrow(&self) -> &str {
&self.0
}
}
fn main() {
let container = StringContainer(String::from("hello"));
let s: &str = container.borrow();
println!("{}", s); // hello
}

标准库已实现的 Borrow:

  • String 实现了 Borrow<str>
  • Vec<T> 实现了 Borrow<[T]>
  • Box<T> 实现了 Borrow<T>

3. Borrow 的使用场景 - HashMap 查找

Section titled “3. Borrow 的使用场景 - HashMap 查找”

Borrow 的主要用途是提供更灵活的查找接口:

use std::collections::HashMap;
use std::borrow::Borrow;
fn main() {
let mut map: HashMap<String, Vec<u8>> = HashMap::new();
map.insert(String::from("apple"), vec![1, 2, 3]);
map.insert(String::from("banana"), vec![4, 5, 6]);
// 使用 &str 查找 HashMap<String, V>
let values = map.get("apple");
println!("{:?}", values); // Some([1, 2, 3])
// 原理:&str 实现了 Borrow<str>,可以与 String 比较
}

BorrowMut 是 Borrow 的可变版本:

trait 定义:

pub trait BorrowMut<Borrowed> {
fn borrow_mut(&mut self) -> &mut Borrowed;
}

实现示例:

use std::borrow::BorrowMut;
#[derive(Debug)]
struct Counter {
count: u32,
}
impl Counter {
fn increment(&mut self) {
self.count += 1;
}
}
impl BorrowMut<u32> for Counter {
fn borrow_mut(&mut self) -> &mut u32 {
&mut self.count
}
}
fn main() {
let mut counter = Counter { count: 0 };
let count_ref: &mut u32 = counter.borrow_mut();
*count_ref += 1;
println!("{}", counter.count); // 1
}

Cow 是一个枚举,代表”要么借用、要么拥有”的数据:

类型定义:

pub enum Cow<'a, T>
where
T: 'a + ToOwned + ?Sized,
{
Borrowed(&'a T),
Owned(T::Owned),
}

核心特性:

  • Borrowed 变体:持有数据的引用
  • Owned 变体:持有数据的所有权
  • to_owned 方法:必要时将借用转为拥有
  • Owned 实现了 Deref,可以像引用一样使用
use std::borrow::Cow;
fn main() {
// 借用情况
let text = "hello world";
let cow: Cow<str> = Cow::Borrowed(text);
println!("{}", cow); // hello world
// 修改情况(会克隆)
let mut cow: Cow<str> = Cow::Borrowed("hello");
cow.to_mut().push_str(" world");
println!("{}", cow); // hello world
// 注意:此时 cow 变成了 Owned
// 直接拥有
let owned: Cow<str> = Cow::Owned(String::from("owned data"));
println!("{}", owned); // owned data
}

Cow 常用于函数参数,实现”接受引用或拥有值”的灵活接口:

use std::borrow::Cow;
fn print_uppercase(s: Cow<str>) {
println!("{}", s.to_uppercase());
}
fn main() {
// 传入 &str(借用)
print_uppercase(Cow::Borrowed("hello"));
// 传入 String(拥有)
print_uppercase(Cow::Owned(String::from("world")));
// 传入字面量
print_uppercase(Cow::Borrowed("foo"));
}
use std::borrow::Cow;
fn clean_whitespace<'a>(input: Cow<'a, str>) -> Cow<'a, str> {
if input.contains(' ') {
// 需要修改,返回新的 owned 字符串
Cow::Owned(input.trim().to_string())
} else {
// 不需要修改,保持借用
input
}
}
fn main() {
// 借用情况
let input = " hello ";
let result = clean_whitespace(Cow::Borrowed(input));
println!("result: '{}'", result);
// 拥有情况
let input = String::from(" world ");
let result = clean_whitespace(Cow::Owned(input));
println!("result: '{}'", result);
}

ToOwned trait 提供了从借用数据创建拥有数据的能力:

trait 定义:

pub trait ToOwned {
type Owned: Borrow<Self>;
fn to_owned(&self) -> Self::Owned;
fn clone_into(&self, target: &mut Self::Owned) { ... }
}

示例:

use std::borrow::ToOwned;
fn main() {
// str -> String
let s: &str = "hello";
let owned: String = s.to_owned();
// [i32] -> Vec<i32>
let slice: &[i32] = &[1, 2, 3];
let owned: Vec<i32> = slice.to_owned();
// str -> Cow<str>
let cow: std::borrow::Cow<str> = s.to_owned();
}

场景1:实现灵活的 API

use std::borrow::Cow;
use std::collections::HashMap;
fn get_value<'a>(map: &HashMap<String, String>, key: &'a str) -> Option<&'a str> {
map.get(key).map(|s| s.as_str())
}
fn main() {
let mut map = HashMap::new();
map.insert("key".to_string(), "value".to_string());
// 使用 &str 查找
if let Some(v) = get_value(&map, "key") {
println!("{}", v);
}
}

场景2:减少不必要的克隆

use std::borrow::Cow;
fn process<'a>(input: Cow<'a, [u8]>) -> usize {
// 只需要读取权限
input.len()
}
fn main() {
// 传入 slice(零拷贝)
let data = vec![1, 2, 3, 4, 5];
let result = process(Cow::Borrowed(&data));
// 传入 owned 数据
let owned_data = vec![6, 7, 8, 9];
let result = process(Cow::Owned(owned_data));
}
  1. Borrow vs AsRef:Borrow 强调”等价性”(相同的哈希值、相等性),AsRef 更通用。

  2. Cow 的 Deref 实现:可以直接调用 Cow<str> 的字符串方法。

  3. BorrowMut 的限制:不是所有类型都实现了 BorrowMut,特别是 DST(dynamically sized types)。

  4. Cow::into_owned:可以使用 into_owned() 强制获取所有权。

  5. 性能考量:Cow 在需要修改时才克隆,避免了不必要的分配。

std::borrow 模块提供了 Rust 借用系统的核心抽象:

类型/Trait作用
Borrow<T>泛型借用 trait
BorrowMut<T>可变借用 trait
Cow<'a, T>写时复制的枚举类型
ToOwned从借用创建拥有值的 trait

合理使用这些抽象可以让你的 API 更加灵活,性能更好。