【Rust投稿】捋捋 Rust 中的 impl Trait 和 dyn Trait

本文来自 PrivateRookie 的知乎投稿:https://zhuanlan.zhihu.com/p/109990547


缘起

一切都要从年末换工作碰上特殊时期, 在家闲着无聊又读了几首诗, 突然想写一个可以浏览和背诵诗词的 TUI 程序说起. 我选择了 Cursive 这个 Rust TUI 库. 在实现时有这么一个函数, 它会根据参数的不同返回某个组件(如 Button, TextView 等). 在 Cursive 中, 每个组件都实现了 View 这个 trait, 最初这个函数只会返回某个确定的组件, 所以函数签名可以这样写

fn some_fn(param: SomeType) -> Button

随着开发进度增加, 这个函数需要返回 Button, TextView 等组件中的一个, 我下意识地写出了类似于下面的代码

fn some_fn(param1: i32, param2: i32) -> impl View {    if param1 > param2 {        // do something...
        return Button {};    } else {        // do something...
        return TextView {};    }}

可惜 Rust 编译器一如既往地打脸, Rust 编译器报错如下

--> src\main.rs:19:16
   |
13 | fn some_fn(param1: i32, param2: i32) -> impl View {
   |                                         --------- expected because this return type...
...
16 |         return Button {};
   |                --------- ...is found to be `Button` here
...
19 |         return TextView {};
   |                ^^^^^^^^^^^ expected struct `Button`, found struct `TextView`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.

从编译器报错信息看函数返回值虽然是 impl View 但其从 if 分支推断返回值类型为 Button 就不再接受 else 分支返回的 TextView. 这与 Rust 要求 if else 两个分支的返回值类型相同的特性一致. 那能不能让函数返回多种类型呢? Rust 之所以要求函数不能返回多种类型是因为 Rust 在需要在 编译期确定返回值占用的内存大小, 显然不同类型的返回值其内存大小不一定相同. 既然如此, 把返回值装箱, 返回一个胖指针, 这样我们的返回值大小可以确定了, 这样也许就可以了吧. 尝试把函数修改成如下形式:

fn some_fn(param1: i32, param2: i32) -> Box {    if param1 > param2 {        // do something...
        return Box::new(Button {});    } else {        // do something...
        return Box::new(TextView {});    }}

现在代码通过编译了, 但如果使用 Rust 2018, 你会发现编译器会抛出警告:

warning: trait objects without an explicit `dyn` are deprecated
  --> src\main.rs:13:45
   |
13 | fn some_fn(param1: i32, param2: i32) -> Box {
   |                                             ^^^^ help: use `dyn`: `dyn View`
   |
   = note: &

你可能感兴趣的:(【Rust投稿】捋捋 Rust 中的 impl Trait 和 dyn Trait)