我們先來看這樣一段代碼:
1impl View for Button { ... }
2
3impl View for Text { ... }
我們看到Button和Text都實現了View屬性, 抽象是一種不錯的設計程序的方法, 幫助我們透明化的使用外部提供的API. 然後我們可能會下意識的寫出下面的代碼:
1/// 這種代碼實際上會讓人感到疑惑. View究竟是個特性還是一個對象.
2/// 這裡的View是一個類型, 所以我們需要寫成 `impl View`.
3/// 不過`impl View` 不能用於多個trait實現的返回. 但是可以作為入參.
4pub fn something() -> View {
5 if ... {
6 Button { ... }
7 } else {
8 Text { ... }
9 }
10}
這段代碼無法通過編譯, 原因就是返回值View需要在編譯器確認大小. 我們需要把它裝成一個胖指針.
1pub fn something() -> Box<View> {...}
嗯,這樣就好很多. 但是編譯器會爆種, 提出一個警告, 希望你把Box<View>改為Box<dyn View>.
這又是什麼意思?
dyn 是動態的縮寫, 意義其實很明顯. 使用dyn修飾的類型, 會在程序執行期動態分發. 會有一定的RUNTIME開銷.
1pub fn something() -> Box<dyn View> {...}
現在我們把代碼改成這樣, 好多了.
接下來說說impl, 這個語法是個語法糖其實.怎麼個語法糖呢?
1fn something<T: View>(v: T) { ... }
2
3// ------------- 用 impl 之後 ------------------
4
5fn something(v: impl View) { ... }
真的就這麼簡單.
第二次說一遍, 這種T(泛型)寫法在入參可以這麼做, 但是返回值不行. 除非你的返回值只返回一個T實現. 兩種以上請重載 (我亂說的,rust可能不支持)或者使用dyn.