Ch 5: std::borrow - 借用与所有权
std::borrow 模块提供了 Rust 中关于借用的核心 trait 和类型。理解这些抽象对于编写灵活的 API、设计库代码、以及深入理解 Rust 的所有权系统至关重要。本章将讲解 Borrow、BorrowMut 和 Cow 三个核心概念。
2. Borrow trait - 通用借用
Section titled “2. Borrow trait - 通用借用”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 比较}4. BorrowMut trait - 可变借用
Section titled “4. BorrowMut trait - 可变借用”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}5. Cow - 写时复制借用
Section titled “5. Cow - 写时复制借用”Cow 是一个枚举,代表”要么借用、要么拥有”的数据:
类型定义:
pub enum Cow<'a, T>where T: 'a + ToOwned + ?Sized,{ Borrowed(&'a T), Owned(T::Owned),}核心特性:
Borrowed变体:持有数据的引用Owned变体:持有数据的所有权to_owned方法:必要时将借用转为拥有Owned实现了Deref,可以像引用一样使用
6. Cow 的基本用法
Section titled “6. Cow 的基本用法”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}7. Cow 在函数参数中的应用
Section titled “7. Cow 在函数参数中的应用”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"));}8. Cow 在字符串处理中的应用
Section titled “8. Cow 在字符串处理中的应用”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);}9. ToOwned trait - 克隆为拥有
Section titled “9. ToOwned trait - 克隆为拥有”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();}10. 实际应用场景
Section titled “10. 实际应用场景”场景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));}11. 需要注意的点
Section titled “11. 需要注意的点”-
BorrowvsAsRef:Borrow强调”等价性”(相同的哈希值、相等性),AsRef更通用。 -
Cow的Deref实现:可以直接调用Cow<str>的字符串方法。 -
BorrowMut的限制:不是所有类型都实现了BorrowMut,特别是 DST(dynamically sized types)。 -
Cow::into_owned:可以使用into_owned()强制获取所有权。 -
性能考量:
Cow在需要修改时才克隆,避免了不必要的分配。
12. 总结
Section titled “12. 总结”std::borrow 模块提供了 Rust 借用系统的核心抽象:
| 类型/Trait | 作用 |
|---|---|
Borrow<T> | 泛型借用 trait |
BorrowMut<T> | 可变借用 trait |
Cow<'a, T> | 写时复制的枚举类型 |
ToOwned | 从借用创建拥有值的 trait |
合理使用这些抽象可以让你的 API 更加灵活,性能更好。