oop – 為什麼Rust不支援trait物件upcasting?
給出這個程式碼:
trait Base { fn a(&self); fn b(&self); fn c(&self); fn d(&self); } trait Derived : Base { fn e(&self); fn f(&self); fn g(&self); } struct S; impl Derived for S { fn e(&self) {} fn f(&self) {} fn g(&self) {} } impl Base for S { fn a(&self) {} fn b(&self) {} fn c(&self) {} fn d(&self) {} }
不幸的是,我不能將& Derived投入到& Base.我想知道為什麼會這樣,因為Derived vtable必須以某種方式引用Base方法.
那麼檢查LLVM IR顯示如下:
@vtable4 = internal unnamed_addr constant { void (i8*)*, i64, i64, void (%struct.S*)*, void (%struct.S*)*, void (%struct.S*)*, void (%struct.S*)* } { void (i8*)* @_ZN2i813glue_drop.98717h857b3af62872ffacE, i64 0, i64 1, void (%struct.S*)* @_ZN6S.Base1a20h57ba36716de00921jbaE, void (%struct.S*)* @_ZN6S.Base1b20h3d50ba92e362d050pbaE, void (%struct.S*)* @_ZN6S.Base1c20h794e6e72e0a45cc2vbaE, void (%struct.S*)* @_ZN6S.Base1d20hda31e564669a8cdaBbaE } @vtable26 = internal unnamed_addr constant { void (i8*)*, i64, i64, void (%struct.S*)*, void (%struct.S*)*, void (%struct.S*)*, void (%struct.S*)*, void (%struct.S*)*, void (%struct.S*)*, void (%struct.S*)* } { void (i8*)* @_ZN2i813glue_drop.98717h857b3af62872ffacE, i64 0, i64 1, void (%struct.S*)* @_ZN9S.Derived1e20h9992ddd0854253d1WaaE, void (%struct.S*)* @_ZN9S.Derived1f20h849d0c78b0615f092aaE, void (%struct.S*)* @_ZN9S.Derived1g20hae95d0f1a38ed23b8aaE, void (%struct.S*)* @_ZN6S.Base1a20h57ba36716de00921jbaE, void (%struct.S*)* @_ZN6S.Base1b20h3d50ba92e362d050pbaE, void (%struct.S*)* @_ZN6S.Base1c20h794e6e72e0a45cc2vbaE, void (%struct.S*)* @_ZN6S.Base1d20hda31e564669a8cdaBbaE }
所有Rust vtables在第一個欄位中包含指向解構函式,大小和對齊的指標,並且在引用supertrait方法時,子專案不會重複它們,也不使用間接引用supertrait vtables.他們只是逐字地複製方法指標,沒有別的.
鑑於這種設計,很容易理解為什麼這不起作用.需要在執行時構建一個新的vtable,它可能位於堆疊上,而不是一個優雅(或最優)的解決方案.
有一些解決方法,當然,像新增顯式的upcast方法到介面,但這需要相當多的樣板(或巨集狂風)才能正常工作.
現在的問題是 – 為什麼它不能以某種方式實現,這樣可以使特徵物件上傳?就像在超級桌上的超級桌上新增一個指標.現在,Rust的動態排程似乎不能滿足ofollow,noindex" target="_blank">LSP ,這是面向物件設計的一個非常基本的原則.
當然,您可以使用靜態排程,這在Rust中非常優雅,但是它容易導致程式碼膨脹,這有時比計算效能更重要 – 比如嵌入式系統,Rust開發人員聲稱支援這樣的用例語言.此外,在許多情況下,您可以成功使用不是純OO的模型,這似乎被Rust的功能設計所鼓勵.仍然,Rust支援許多有用的OO模式…所以為什麼不是LSP?
有沒有人知道這樣的設計的理由?
其實我覺得我有理由我發現了一種優雅的方法來增加對任何需要的特徵的支援,而程式員也可以選擇是否將該額外的vtable條目新增到特徵中,或者不喜歡,這是一個類似的權衡C的虛擬與非虛擬方法:優雅和模型的正確性與效能.
程式碼可以實現如下:
trait Base : AsBase { ... } trait AsBase { fn as_base(&self) -> &Base; } impl<T: Base> AsBase for T { fn as_base(&self) -> &Base { self } }
當然,可以新增一些額外的方法來投射&mut指標或者一個Box(新增一個T必須是一個靜態型別的要求),但這是一個一般的想法.這允許安全和簡單(雖然不是隱含的)每個派生型別的上傳,沒有每個派生型別的樣板.
http://stackoverflow.com/questions/28632968/why-doesnt-rust-support-trait-object-upcasting