rust语言学习笔记Trait(十三)Borrow、BorrowMut(借用)

BorrowBorrowMut 是 Rust 标准库中用于抽象‌借用行为 ‌的两个核心 trait,位于 std::borrow 模块。它们为类型系统提供了一种"可以通过引用访问底层数据"的通用契约,尤其适合泛型编程和集合类型的键查找场景。‌

1、Borrow 不可变借用

(1)定义

rust 复制代码
pub trait Borrow<Borrowed: ?Sized> {
    fn borrow(&self) -> &Borrowed;
}

Borrow<Borrowed> 表示"当前类型可以被不可变地借用为 &Borrowed"。

  • String 实现了 Borrow<str>,即 String 可以借用为 &str
  • Vec<T> 实现了 Borrow<[T]>,即 Vec<T> 可以借用为 &[T]
  • Box<T> 实现了 Borrow<T>,即 Box<T> 可以借用为 `&T

(2)核心特点:语义等价保证

Borrow 有一个‌文档规定的契约 ‌:如果类型 T 实现了 Borrow<U>,那么 T 的借用结果 &U 必须在 EqOrdHash 这些 trait 上与 T 自身保持行为一致。也就是说,‌借用前后的值是语义等价的‌。

这一约束是 BorrowAsRef 最本质的区别。AsRef 只关注类型转换本身,不关心相等性、哈希值等语义是否一致;而 Borrow 明确要求这种一致性。

(3)为什么是泛型 Trait?

一个类型可能需要被借用为多种形式。例如 String

  • 实现了 Borrow<str>(借用为字符串切片)
  • 通过 blanket impl 自动实现了 Borrow<String>(借用为自身)

这种泛型设计让同一种类型能以不同的"视角"被安全借用,极大提升了 API 的灵活性。

2、BorrowMut 可变借用

(1)定义

复制代码
rustpub trait BorrowMut<Borrowed: ?Sized>: Borrow<Borrowed> {
    fn borrow_mut(&mut self) -> &mut Borrowed;
}

BorrowMutBorrow 的可变版本,并且‌**自动继承了 Borrow**‌。它允许通过可变引用获取底层数据的可变借用。

(2)常见实现

  • &mut T 实现 BorrowMut<T>
  • Vec<T> 实现 BorrowMut<[T]>
  • RefCell<T> 实现 BorrowMut<T>(在运行时检查借用规则)

3、HashMap 等集合的异构键查找(Borrow 的核心用途)

HashMap::get 的签名

rust 复制代码
pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
    where
        K: Borrow<Q>,
        Q: Hash + Eq,
    {
        self.base.get(k)
    }

这里的 K: Borrow<Q> 保证了键类型 K(如 String)可以被借用为查询类型 Q(如 str),并且两者的 HashEq 实现是兼容的,从而确保通过 &str 计算出的哈希桶与插入时通过 String 计算出的哈希桶完全一致。

rust 复制代码
use std::collections::HashMap;

fn main() {
    let mut m = HashMap::new();
    m.insert("aaa".to_string(), 111);
    println!("{:?}", m.get("aaa"));     // Some(111)
}

同样的机制也适用于 BTreeMapHashSetBTreeSet 等标准库集合类型。

4、泛型函数参数的类型扩展

当函数需要接受多种"可借用为某类型"的参数时,Borrow 可以完美胜任:

rust 复制代码
use std::borrow::Borrow;

fn print_info<T: Borrow<str>>(s: T) {
    let st = s.borrow();                      // &str
    println!("{}", st);
}

fn main() {
    print_info("aaaaa");                             // &str
    print_info(String::from("bbbbb"));               // String
    print_info(std::borrow::Cow::Borrowed("ccc"));   // Cow<str>
}

函数只需要一个实现,就能同时接受 &strStringCow<str> 等多种类型,且所有类型在 Hash/Eq/Ord 上的行为保持一致。

5、自定义类型的语义借用

当自定义类型包装了某个值,且希望它在集合中表现得如同底层类型一样时,可以实现 Borrow

rust 复制代码
use std::borrow::Borrow;
use std::collections::HashSet;
use std::hash::Hash;

#[derive(PartialEq, Eq, Hash)]
struct MyStruct(String);


impl Borrow<str> for MyStruct {
    fn borrow(&self) -> &str {
        &self.0
    }
}

fn main() {
    let mut s = HashSet::new();
    s.insert(MyStruct("aaeeaa".to_string()));
    println!("{}", s.contains("aaeeaa"));
}

6、BorrowMut 在可变上下文中的应用

当需要泛型地修改包装类型的内部值时:

rust 复制代码
use std::borrow::BorrowMut;

fn my_fn<T: BorrowMut<i32>>(s: &mut T) {
    *s.borrow_mut() += 2;
}

fn main() {
    let mut a = 6;
    my_fn(&mut a);
    println!("{}", a);        // 8

    let mut b = Box::new(15);
    my_fn(&mut b);
    println!("{}", *b);       // 17
}

函数不关心传入的是 &mut i32 还是 Box<i32>,只要它能通过 BorrowMut<i32> 提供对 i32 的可变引用即可。

7、 Borrow vs AsRef 的选择指南

维度 Borrow AsRef
语义保证 要求借用值与原值在 Eq/Hash/Ord 上等价 仅做类型转换,无语义保证
主要用途 集合键查找、需要语义等价的场合 简单的引用转换、文件路径处理
典型场景 HashMap::getBTreeMap 键查询 File::open、接受 Path 参数的函数
实现约束 需确保 x.borrow() == y.borrow() 当且仅当 x == y 无此约束

简单记忆 ‌:涉及集合查找、比较、哈希等价时用 Borrow;仅是类型转换、路径处理等用 AsRef

相关推荐
红尘散仙8 小时前
想写一个像样的终端 App?试试把 React 的开发体验搬进 Rust TUI
前端·rust
vivo互联网技术9 小时前
从 Web 到桌面:基于 Tauri 2.0 + Vue 3 打造 vivo 线下门店「大头贴」拍照体验系统
前端·rust
Rust研习社12 小时前
这 8 个 Rust 学习资源值得每个新手收藏起来
后端·rust·编程语言
通信小呆呆1 天前
当算法有了“五感”:多模态数据融合如何向人体感官协同学习?
人工智能·学习·算法·机器学习·机器人
H__Rick1 天前
自动对焦学习-3
人工智能·学习·计算机视觉
Daisy Lee1 天前
量化学习-第1章-什么是量化金融
学习·金融·datawhale
Alsn861 天前
等待学习-学习目录:Docker 容器安全攻防
学习·安全·docker
星栈1 天前
10 分钟跑起第一个 Dioxus 应用:`dx` CLI、`rsx!` 和热更新好不好用
前端·rust·前端框架
YM52e1 天前
买菜计算器小应用 - HarmonyOS ArkUI 开发实战-PC版本
学习·华为·harmonyos·鸿蒙·鸿蒙系统
小雨下雨的雨1 天前
HarmonyOS ArkUI训练营入门-组件掌握系列-Animation 动画效果实现-PC版本
学习·华为·harmonyos·鸿蒙