Borrow、BorrowMut(借用))
Borrow和BorrowMut是 Rust 标准库中用于抽象借用行为的两个核心 trait位于std::borrow模块。它们为类型系统提供了一种“可以通过引用访问底层数据”的通用契约尤其适合泛型编程和集合类型的键查找场景。1、Borrow不可变借用1定义pubtraitBorrowBorrowed:?Sized{fnborrow(self)-Borrowed;}BorrowBorrowed表示“当前类型可以被不可变地借用为Borrowed”。String实现了Borrowstr即String可以借用为strVecT实现了Borrow[T]即VecT可以借用为[T]BoxT实现了BorrowT即BoxT可以借用为 T2核心特点语义等价保证Borrow有一个文档规定的契约如果类型T实现了BorrowU那么T的借用结果U必须在Eq、Ord、Hash这些 trait 上与T自身保持行为一致。也就是说借用前后的值是语义等价的。这一约束是Borrow与AsRef最本质的区别。AsRef只关注类型转换本身不关心相等性、哈希值等语义是否一致而Borrow明确要求这种一致性。3为什么是泛型 Trait一个类型可能需要被借用为多种形式。例如String实现了Borrowstr借用为字符串切片通过 blanket impl 自动实现了BorrowString借用为自身这种泛型设计让同一种类型能以不同的“视角”被安全借用极大提升了 API 的灵活性。2、BorrowMut可变借用1定义rustpub trait BorrowMutBorrowed: ?Sized: BorrowBorrowed { fn borrow_mut(mut self) - mut Borrowed; }BorrowMut是Borrow的可变版本并且**自动继承了Borrow**。它允许通过可变引用获取底层数据的可变借用。2常见实现mut T实现BorrowMutTVecT实现BorrowMut[T]RefCellT实现BorrowMutT在运行时检查借用规则3、HashMap等集合的异构键查找Borrow的核心用途HashMap::get的签名pubfngetQ:?Sized(self,k:Q)-OptionVwhereK:BorrowQ,Q:HashEq,{self.base.get(k)}这里的K: BorrowQ保证了键类型K如String可以被借用为查询类型Q如str并且两者的Hash和Eq实现是兼容的从而确保通过str计算出的哈希桶与插入时通过String计算出的哈希桶完全一致。usestd::collections::HashMap;fnmain(){letmutmHashMap::new();m.insert(aaa.to_string(),111);println!({:?},m.get(aaa));// Some(111)}同样的机制也适用于BTreeMap、HashSet、BTreeSet等标准库集合类型。4、泛型函数参数的类型扩展当函数需要接受多种“可借用为某类型”的参数时Borrow可以完美胜任usestd::borrow::Borrow;fnprint_infoT:Borrowstr(s:T){letsts.borrow();// strprintln!({},st);}fnmain(){print_info(aaaaa);// strprint_info(String::from(bbbbb));// Stringprint_info(std::borrow::Cow::Borrowed(ccc));// Cowstr}函数只需要一个实现就能同时接受str、String、Cowstr等多种类型且所有类型在Hash/Eq/Ord上的行为保持一致。5、自定义类型的语义借用当自定义类型包装了某个值且希望它在集合中表现得如同底层类型一样时可以实现Borrowusestd::borrow::Borrow;usestd::collections::HashSet;usestd::hash::Hash;#[derive(PartialEq, Eq, Hash)]structMyStruct(String);implBorrowstrforMyStruct{fnborrow(self)-str{self.0}}fnmain(){letmutsHashSet::new();s.insert(MyStruct(aaeeaa.to_string()));println!({},s.contains(aaeeaa));}6、BorrowMut在可变上下文中的应用当需要泛型地修改包装类型的内部值时usestd::borrow::BorrowMut;fnmy_fnT:BorrowMuti32(s:mutT){*s.borrow_mut()2;}fnmain(){letmuta6;my_fn(muta);println!({},a);// 8letmutbBox::new(15);my_fn(mutb);println!({},*b);// 17}函数不关心传入的是mut i32还是Boxi32只要它能通过BorrowMuti32提供对i32的可变引用即可。7、BorrowvsAsRef的选择指南维度BorrowAsRef语义保证要求借用值与原值在Eq/Hash/Ord上等价仅做类型转换无语义保证主要用途集合键查找、需要语义等价的场合简单的引用转换、文件路径处理典型场景HashMap::get、BTreeMap键查询File::open、接受Path参数的函数实现约束需确保x.borrow() y.borrow()当且仅当x y无此约束简单记忆涉及集合查找、比较、哈希等价时用Borrow仅是类型转换、路径处理等用AsRef。