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

Borrow 和 BorrowMut 是 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 必须在 Eq、Ord、Hash 这些 trait 上与 T 自身保持行为一致。也就是说,‌借用前后的值是语义等价的‌。

这一约束是 Borrow 与 AsRef 最本质的区别。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;
}

BorrowMut 是 Borrow 的可变版本,并且‌**自动继承了 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),并且两者的 Hash 和 Eq 实现是兼容的,从而确保通过 &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)
}

同样的机制也适用于 BTreeMap、HashSet、BTreeSet 等标准库集合类型。

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>
}

函数只需要一个实现,就能同时接受 &str、String、Cow<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::get、BTreeMap 键查询 File::open、接受 Path 参数的函数
实现约束 需确保 x.borrow() == y.borrow() 当且仅当 x == y 无此约束

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

相关推荐
旖旎夜光6 分钟前
LeetCode 215: 数组中的第K个最大元素 (快速排序) —— 题解
c++·学习·算法·leetcode·力控
weixin_448119943 小时前
Datawhale DeepSeek Harness 技术入门与架构原理 笔记4
笔记·架构
小灰灰搞电子12 小时前
Rust+Slint 实现FTP客户端调试助手源码分享
开发语言·后端·rust
外收内放12 小时前
面向对象高级(实战案例)
开发语言·python·学习
Flynt13 小时前
Cloudflare 用 Rust 重写了 TypeScript:我拿同一批代码跑了跑,报错一模一样,退出码不一样
rust·typescript·前端工程化
薛定e的猫咪13 小时前
(ICLR 2020)MAML 靠的不是快速学习,而是特征复用
人工智能·深度学习·学习·算法·机器学习
薛定e的猫咪13 小时前
(ICLR 2026)DF-META:面向 FJSP 的无导数元学习实例自适应调度
人工智能·深度学习·学习·算法·机器学习
果壳science15 小时前
张祥前实验如何获取正电荷
笔记·科技·嵌入式硬件·硬件工程·制造
大叔学Linux16 小时前
FreeRTOS任务切换笔记
arm开发·笔记
别动我齐刘海17 小时前
机器人 C++ / 系统联调补强2——异常处理、Protobuf
linux·c++·人工智能·学习·算法·机器学习·机器人