<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                當涉及到多態的代碼時,我們需要一個機制來決定哪個具體的版本應該得到執行。這叫做“分發”(dispatch)。大體上有兩種形式的分發:靜態分發和動態分發。雖然Rust喜歡靜態分發,不過它也提供了一個叫做“trait對象”的機制來支持動態分發。 ## 背景 在本章接下來的內容中,我們需要一個trait和一些實現。讓我們來創建一個簡單的`Foo`。它有一個返回`String`的方法。 ~~~ trait Foo { fn method(&self) -> String; } ~~~ 我們也在`u8`和`String`上實現了這個特性: ~~~ impl Foo for u8 { fn method(&self) -> String { format!("u8: {}", *self) } } impl Foo for String { fn method(&self) -> String { format!("string: {}", *self) } } ~~~ ## 靜態分發 我們可以使用trait的限制來進行靜態分發: ~~~ fn do_something<T: Foo>(x: T) { x.method(); } fn main() { let x = 5u8; let y = "Hello".to_string(); do_something(x); do_something(y); } ~~~ 在這里Rust用“單態”來進行靜態分發。這意味著Rust會為`u8`和`String`分別創建一個特殊版本的的`do_something()`,然后將對`do_something`的調用替換為這些特殊函數。也就是說,Rust生成了一些像這樣的函數: ~~~ fn do_something_u8(x: u8) { x.method(); } fn do_something_string(x: String) { x.method(); } fn main() { let x = 5u8; let y = "Hello".to_string(); do_something_u8(x); do_something_string(y); } ~~~ 這有一個很牛的好處:靜態分發允許函數被內聯調用,因為調用者在編譯時就知道它,內聯對編譯器進行代碼優化十分有利。靜態分發能提高程序的運行效率,不過相應的也有它的弊端:會導致“代碼膨脹”(code bloat)。因為在編譯出的二進制程序中,同樣的函數,對于每個類型都會有不同的拷貝存在。 此外,編譯器也不是完美的并且“優化”后的代碼可能更慢。例如,過度的函數內聯會導致指令緩存膨脹(緩存控制著我們周圍的一切)。這也是為何要謹慎使用`#[inline]`和`#[inline(always)]`的部分原因。另外一個使用動態分發的原因是,在一些情況下,動態分發更有效率。 然而,常規情況下靜態分發更有效率,并且我們總是可以寫一個小的靜態分發的封裝函數來進行動態分發,不過反過來不行,這就是說靜態調用更加靈活。因為這個原因標準庫盡可能的使用了靜態分發。 ## 動態分發 Rust通過一個叫做“trait對象”的功能提供動態分發。比如說`&Foo`、`Box`這些就是trait對象。它們是一些值,值中儲存實現了特定trait的_任意_類型。它的具體類型只能在運行時才能確定。 從一些實現了特定`trait`的類型的指針中,可以從通過_轉型_(casting)(例如,`&x as &Foo`)或者_強制轉型_(coercing it)(例如,把`&x`當做參數傳遞給一個接收`&Foo`類型的函數)來取得trait對象。 這些trait對象的強制多態和轉型也適用于類似于`&mut Foo`的`&mut T`以及`Box`的`Box`這樣的指針,也就是目前為止我們討論到的所有指針。強制轉型和轉型是一樣的。 這個操作可以被看作“清除”編譯器關于特定類型指針的信息,因此trait對象有時被稱為“類型清除”(type erasure)。 回到上面的例子,我們可以使用相同的trait,通過trait對象的轉型(casting)來進行動態分發: ~~~ fn do_something(x: &Foo) { x.method(); } fn main() { let x = 5u8; do_something(&x as &Foo); } ~~~ 或者通過強制轉型(by concercing): ~~~ fn do_something(x: &Foo) { x.method(); } fn main() { let x = "Hello".to_string(); do_something(&x); } ~~~ 一個使用trait對象的函數并沒有為每個實現了`Foo`的類型專門生成函數:它只有一份函數的代碼,一般(但不總是)會減少代碼膨脹。然而,因為調用虛函數,會帶來更大的運行時開銷,也會大大地阻止任何內聯以及相關優化的進行。 ### 為什用指針? 和很多托管語言不一樣,Rust默認不用指針來存放數據,因此類型有著不同的大小。在編譯時知道值的大小(size),以及了解把值作為參數傳遞給函數、值在棧上移動、值在堆上分配(或釋放)并儲存等情況,對于Rust程序員來說是很重要的。 對于`Foo`,我們需要一個值至少是一個`String`(24字節)或一個`u8`(1字節),或者其它crate中可能實現了`Foo`(任意字節)的其他類型。如果值沒有使用指針存儲,我們無法保證代碼能對其他類型正常運作,因為其它類型可以是任意大小的。 用指針來儲存值意味著當我們使用trait對象時值的大小(size)是無關的,只與指針的大小(size)有關。 ### 表現(Representation) 可以在一個trait對象上通過一個特殊的函數指針的記錄調用的特性函數通常叫做“虛函數表”(由編譯器創建和管理)。 trait對象既簡單又復雜:它的核心表現和設計是十分直觀的,不過這有一些難懂的錯誤信息和詭異行為有待發掘。 讓我們從一個簡單的,帶有trait對象的運行時表現開始。`std::raw`模塊包含與復雜的內建類型有相同結構的結構體,[包括trait對象](http://doc.rust-lang.org/std/raw/struct.TraitObject.html): ~~~ pub struct TraitObject { pub data: *mut (), pub vtable: *mut (), } ~~~ 這就是了,一個trait對象就像包含一個“數據”指針和“虛函數表”指針的`&Foo`。 數據指針指向trait對象保存的數據(某個未知的類型`T`),和一個虛表指針指向對應`T`的`Foo`實現的虛函數表。 一個虛表本質上是一個函數指針的結構體,指向每個函數實現的具體機器碼。一個像`trait_object.method()`的函數調用會從虛表中取出正確的指針然后進行一個動態調用。例如: ~~~ struct FooVtable { destructor: fn(*mut ()), size: usize, align: usize, method: fn(*const ()) -> String, } // u8: fn call_method_on_u8(x: *const ()) -> String { // the compiler guarantees that this function is only called // with `x` pointing to a u8 let byte: &u8 = unsafe { &*(x as *const u8) }; byte.method() } static Foo_for_u8_vtable: FooVtable = FooVtable { destructor: /* compiler magic */, size: 1, align: 1, // cast to a function pointer method: call_method_on_u8 as fn(*const ()) -> String, }; // String: fn call_method_on_String(x: *const ()) -> String { // the compiler guarantees that this function is only called // with `x` pointing to a String let string: &String = unsafe { &*(x as *const String) }; string.method() } static Foo_for_String_vtable: FooVtable = FooVtable { destructor: /* compiler magic */, // values for a 64-bit computer, halve them for 32-bit ones size: 24, align: 8, method: call_method_on_String as fn(*const ()) -> String, }; ~~~ 在每個虛表中的`destructor`字段指向一個會清理虛表類型的任何資源的函數,對于`u8`是普通的,不過對于`String`它會釋放內存。這對于像`Box`這類有所有權的trait對象來說是必要的,它需要在離開作用域后清理`Box`分配和他內部的類型。`size`和`align`字段儲存需要清除類型的大小和它的對齊情況;它們原理上是無用的因為這些信息已經嵌入了析構函數中,不過在將來會被使用到,因為trait對象正日益變得更靈活。 假設我們有一些實現了`Foo`的值,那么顯式的創建和使用`Foo`trait對象可能看起來有點像這個(忽略不匹配的類型,它們只是指針而已): ~~~ let a: String = "foo".to_string(); let x: u8 = 1; // let b: &Foo = &a; let b = TraitObject { // store the data data: &a, // store the methods vtable: &Foo_for_String_vtable }; // let y: &Foo = x; let y = TraitObject { // store the data data: &x, // store the methods vtable: &Foo_for_u8_vtable }; // b.method(); (b.vtable.method)(b.data); // y.method(); (y.vtable.method)(y.data); ~~~ 如果`b`或`y`擁有trait對象(`Box`),在它們離開作用域后會有一個`(b.vtable.destructor)(b.data)`(相應的還有`y`的)調用。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看