Rust 技巧:Borrow Trait
在這篇文章中,我們將探索 Rust trait Borrow,將看到它與類似的 AsRef trait 有何不同。
Borrow 和 AsRef 是什麼?
Borrow 和 AsRef 都是允許我們將其引用轉換爲其他的類型。例如,假設我們有一個 String 對象。可以使用 Borrow 或 AsRef 來獲取對 str 的引用。
let s = String::from("Hello");
let s_ref: &str = s.borrow(); // using Borrow
let s_ref: &str = s.as_ref(); // using AsRef
這是可能的,因爲 String 結構體同時實現了 Borrow 和 AsRef。
看一下上面的函數原型,它們看起來是一樣的,都返回 & str 類型。事實上,它們也產生相同的值:
use std::borrow::Borrow;
fn main() {
let s = String::from("Hello");
let s1: &str = s.borrow(); // using Borrow
let s2: &str = s.as_ref(); // using AsRef
assert_eq!(s1, s2);
}
所以,問題是,它們有什麼不同,什麼時候使用 Borrow?
何時使用 Borrow?
Borrow 是一個 trait,用於作爲另一種類型的規範的借用方式。Borrow 的一個典型用例是,你有一個數據,它封裝了內部類型 T,用於增加功能。例如,String 爲 str 添加了可變性功能,因此就其特性而言,它是 str 的超集。換句話說,String 可以做所有 str 可以做的不可變操作。類似地,Vec 本質上是 [T] 的一個更靈活的版本,所以它同樣可以做 [T] 可以做的所有不可變的事情,以此類推。
實現 Borrow 的一個要求是,借用值的 Hash、Eq 和 Ord 與擁有值的 Hash、Eq 和 Ord 相等。這是與 AsRef trait 的關鍵區別。如果某些數據結構爲 T 實現了 Borrow trait,那麼它的行爲必須與 T 相同。根據這一要求,標準庫 HashMap 的 get() 和 get_mut() 方法使用了 Borrow trait。
注意 K 是鍵,它實現了 Q 的 Borrow trait。K 的典型類型是 String,而 Q 的典型類型是 str。這意味着我們可以傳遞任何可以作爲 K 借用的類型 Q。在我們的例子中,String 和 & str 都實現了 Borrow,所以它們可以用作鍵。
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
let key = String::from("key");
map.insert(key.clone(), 42);
// 我們可以使用&String或&str來獲取值
assert_eq!(map.get("key"), Some(&42)); // &str
assert_eq!(map.get(&key), Some(&42)); // &String
}
Borrow 和 AsRef 之間的第二個關鍵區別是——Borrow 對任何類型 T 都有一個全面的實現,也就是說,不需要爲 & T 實現 Borrow。思考下面的代碼:
struct MyStruct {
val: i64,
}
impl AsRef<i64> for MyStruct {
fn as_ref(&self) -> &i64 {
&self.val
}
}
fn print_i64(x: impl AsRef<i64>) {
println!("{}", x.as_ref());
}
fn main() {
let x = MyStruct { val: 314 };
print_i64(&1);
print_i64(x);
}
我們希望能夠傳遞數字類型 i64 的直接引用,以及任何行爲類似於 i64 引用的類型。不幸的是,這段代碼無法編譯,因爲沒有爲 & i64 實現 AsRef。
error[E0277]: the trait bound `{integer}: AsRef<i64>` is not satisfied
--> src/main.rs:17:16
|
17 | print_i64(&1);
| --------- ^ the trait `AsRef<i64>` is not implemented for `{integer}`
| |
| required by a bound introduced by this call
|
= note: required for `&{integer}` to implement `AsRef<i64>`
note: required by a bound in `print_i64`
--> src/main.rs:11:22
|
11 | fn print_i64(x: impl AsRef<i64>) {
| ^^^^^^^^^^ required by this bound in `print_i64`
我們看看 Borrow Trait。另外,Borrow 可以開箱即用。
use std::borrow::Borrow;
struct MyStruct {
val: i64,
}
impl Borrow<i64> for MyStruct {
fn borrow(&self) -> &i64 {
&self.val
}
}
fn print_i64(x: impl Borrow<i64>) {
println!("{}", x.borrow());
}
fn main() {
let x = MyStruct { val: 314 };
print_i64(&1);
print_i64(x);
}
可以發現,使用 Borrow Trait 編譯通過。以上就是 Borrow Trait 與 AsRef Trait 的區別。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/98OwGZaEFCrQ2krHUbIGfg