最近看到两个trait长得挺像,带着疑惑前来学习一下。
Borrow VS AsRef
Borrow与AsRef是Rust中非常相似的两个trait,分别是:
pub trait Borrow<Borrowed: ?Sized> {
fn borrow(&self) -> &Borrowed;
}
pub trait AsRef<T: ?Sized> {
fn as_ref(&self) -> &T;
}
可以看到这两个从接口层面是一样的。
使用方式如下:
let s = String::from("Hello World");
let s_ref: &str = s.borrow(); // using Borrow
let s_ref: &str = s.as_ref(); // using AsRef
那么问题来的,什么时候使用Borrow,什么时候使用AsRef呢?
1.trait一致性
Borrow
also requires thatHash
,Eq
andOrd
for a borrowed value are equivalent to those of the owned value. For this reason, if you want to borrow only a single field of a struct you can implementAsRef
, but notBorrow
.
Borrow
还要求对于借用值的Hash
、Eq
和 Ord
与拥有值相等。因此,如果你只想借用结构体的单个字段,你可以实现 AsRef
,但不能实现 Borrow
。
如果类型T1实现了Borrow<T2>
,在为T1实现一些特殊的trait(例如:Eq、Ord、Hash)时,其行为应该与T2有相同的行为。
例如:标准库 HashMap 对 get() 和 get_mut() 方法使用了Borrow 特征。K是HashMap的key,而get接收的是k类型是&Q
。
例如:HashMap<String, xx>
,K就是String,Q就是str,这允许我们可以传递任何可以作为 K 借用的类型 Q。通过额外要求 Q: Hash + Eq,它表明要求 K 和 Q 具有产生相同结果的 Hash 和 Eq trait的实现。
impl<K, V> HashMap<K, V> {
pub fn get<Q>(&self, k: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized
{
// ...
}
}
https://doc.rust-lang.org/std/borrow/trait.Borrow.html
2.默认实现
Unlike
AsRef
,Borrow
has a blanket impl for anyT
, and can be used to accept either a reference or a value. (See also note onAsRef
’s reflexibility below.)
与 AsRef
不同,Borrow
为任何 T
都有一个默认实现,并且可以用来接受引用或值。
例如:使用Borrow下面代码可以正常工作。
use std::borrow::Borrow;
fn print_value<T: Borrow<i32>>(value: T) {
println!("Value is: {}", value.borrow());
}
fn main() {
let number = 42;
print_value(&number);
print_value(number);
}
而如果使用AsRef,则不可以,报错:
the trait `std::convert::AsRef<i32>` is not implemented for `{integer}`
示例:
fn print_value<T: AsRef<i32>>(value: T) {
println!("Value is: {}", value.as_ref());
}
fn main() {
let number = 42;
print_value(&number);
print_value(number);
}
https://doc.rust-lang.org/std/convert/trait.AsRef.html#relation-to-borrow
往期回顾:
我的春招求职面经
热度更新,手把手实现工业级线程池