Rust?中?Deref?Coercion講解
0x00 前言
寫這個文檔的初衷是因為在中文社區(qū)看到一個非常不負責任的翻譯,將 “implicit deref coercion” 翻譯成 “隱式 deref 強制”,于是覺得有必要記錄一下,以防止后來的新手在這里翻車。
首先需要解釋一下 “coercion” 在編程語言文檔中尤其是涉及到類型轉換時的中文意思。
類型轉換 (type conversion) 包括顯式指定被轉換到的類型的顯式轉換 (explicit conversion) 或稱 cast,以及與之相對的隱式轉換 (implicit conversion) 或稱 coercion。因為翻譯不準等原因,這兩者之間經常被混淆。
cast 和 coercion 同時出現(xiàn)的時候,中文把前者翻譯成 “顯式類型轉換”,后者翻譯成 “隱式類型轉換”。
Rust 的設計理念一向是顯式比隱式好,也就是說所有的行為盡量在代碼中表現(xiàn)出來。但也是有例外的,例如接下來我們要討論的兩個話題。
0x01 Deref Trait
Rust 中有一對運算符 & 和 *,分別表示對一個變量的引用和解引用。
例如下面的代碼:
fn main() {
let x = 5;
let y = &x;
assert_eq!(5, x);
assert_eq!(5, *y);
}
如果按照 C 語言的思維方式,這兩個操作符是互補的,即互為逆操作,但在 Rust 中并不適用。當一個類型實現(xiàn)了 Deref 這個 Trait 后,對該類型顯式執(zhí)行 *y 操作時,Rust 將隱式執(zhí)行 *(y.deref()) 。
如果沒有實現(xiàn) Deref trait,只有引用類型可以被解引用,而實現(xiàn)了 Deref trait 后,編譯器將隱式執(zhí)行 deref 決定返回怎樣的引用類型。
Deref Trait 的定義如下:
pub trait Deref {
type Target: ?Sized;
fn deref(&self) -> &Self::Target;
}
有一個很有趣的點,雖然是 Deref 操作,但是返回類型卻是一個引用類型。這個在官方文檔中給出了解釋:
The reason the deref method returns a reference to a value, and that the plain dereference outside the parentheses in *(y.deref()) is still necessary, is to do with the ownership system. If the deref method returned the value directly instead of a reference to the value, the value would be moved out of self. We don’t want to take ownership of the inner value inside MyBox in this case or in most cases where we use the dereference operator.
主要原因是為了與大多數場景下的 ownership 機制適配。
通過了解 Rust 的 Deref Trait 后,我們可以得出結論:在 Rust 中 *&T 和 &*T 不一定是同一個類型,例如下面示例:
use std::ops::Deref;
#[derive(Copy, Clone, Debug)]
struct MyBox {
alpha: i32,
}
impl Deref for MyBox {
type Target = i32;
fn deref(&self) -> &Self::Target {
&self.alpha
}
}
fn main() {
let beta = MyBox { alpha: 32 };
let gamma = &*beta; // &i32
let delta = *β // Mybox
println!("{:?}, {:?}", gamma, delta);
}
0x02 Implicit Deref Coercion
Deref coercion converts a reference to a type that implements the Deref trait into a reference to another type.
最初接觸到 Deref coercion 的場景通常是使用 &str 作為參數的函數,傳入 &String 是可以編譯通過的,習慣了 Rust 大爺苛刻的編譯器后覺得這不可思議,深入了解后才知道,Rust 內部實現(xiàn)了 String 類型的 Deref trait,并且 type Target = &str 。
當引用作為函數或方法的參數時,Rust 編譯器將隱式執(zhí)行 deref coercion,以找到適配的類型。這樣省去了 * 和 & 的繁瑣操作,但講真對新手確實不友好。下面是一個示例:
fn hello(name: &str) {
println!("Hello, {name}!");
}
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
use std::ops::Deref;
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
fn main() {
let m = MyBox::new(String::from("Rust"));
hello(&m);
}
再看一個有意思的示例:
fn main() {
let s = "Hello, Rust!";
println!("length: {}", s.len());
println!("length: {}", (&s).len());
println!("length: {}", (&&&&&&&&&&&&&s).len());
}
上面的示例是可以編譯通過的,我們如果使用 &&&&&&&&&&str 類型來調用成員方法,也是可以的。原因是 Rust 編譯器的 deref coercion 是遞歸的,當它找不到這個成員方法的時候會自動嘗試使用 deref() 方法后再找該方法,一直遞歸下去。編譯器在&&&str 類型里面找不到 len 方法,就嘗試將它 deref(),變成 &&str 類型,再尋找 len 方法,還是沒找到,那么繼續(xù) deref(),變成 &str,現(xiàn)在找到 len 方法了,于是就調用這個方法。
0x03 Option 中的 as_deref()
熟練的使用 Option 可以在 Rust 開發(fā)中節(jié)省很多頭發(fā)。當需要將 Option<T> 轉化為 Option<&T> 時,我們通常使用 Option.as_ref() 操作,再結合 map() 方法可以在不轉移所有權的情況下使用 Option 中的變量。當看到 Option.as_deref() 操作時,我們首先會望文生義的認為是將 Option<&T> 轉化為 Option<T>,但理解了前兩節(jié)的內容后會懂得 Option.as_deref() 的操作其實是 Option.as_ref().map(|s| s.deref()) 的簡寫形式,返回的仍然是一個引用,目的是為了使用 Rust 中的 deref coercion 機制。
參考文檔
https://doc.rust-lang.org/std/ops/trait.Deref.html
https://doc.rust-lang.org/book/ch15-02-deref.html
https://stackoverflow.com/questions/8857763/what-is-the-difference-between-casting-and-coercing
https://mp.weixin.qq.com/s/G28XE1rfX0nT6zIi86ji0Q
https://blog.csdn.net/weixin_39702559/article/details/112276392
https://blog.csdn.net/JAN6055/article/details/125774473
到此這篇關于Rust 中 Deref Coercion 介紹的文章就介紹到這了,更多相關Rust Deref Coercion內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
rust?創(chuàng)建多線程web?server的詳細過程
web?server?中主要的兩個協(xié)議是?http?和?tcp,tcp?是底層協(xié)議,http?是構建在?tcp?之上的,本篇文章重點給大家介紹rust?創(chuàng)建多線程web?server的詳細過程,感興趣的朋友跟隨小編一起看看吧2023-11-11

