我們先來看這樣一段代碼:

1impl View for Button { ... }
2
3impl View for Text { ... }

我們看到ButtonText都實現了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.