仓库源文站点原文

仍用 在线环境 运行。看 dyn 的介绍,似乎是在指定函数返回类型时,额外说明“类型不一定”,估计是 dynamic 的缩写。强烈建议关键词尽量避免缩写!至少在官方文档里说清楚。

上来运行了七八遍,寻思怎么老是baaaah?不是随机后看 <0.5 么?再看一眼随机数如何生成。。。命名能不能不那么误导。直接把 random_number 改名为某数,感觉好点。

struct 羊 {}
struct 牛 {}

trait 动物 {
    // Instance method signature
    fn 嘶鸣(&self) -> &'static str;
}

// Implement the `动物` trait for `羊`.
impl 动物 for 羊 {
    fn 嘶鸣(&self) -> &'static str {
        "baaaaah!"
    }
}

// Implement the `动物` trait for `牛`.
impl 动物 for 牛 {
    fn 嘶鸣(&self) -> &'static str {
        "moooooo!"
    }
}

// Returns some struct that implements 动物, but we don't know which one at compile time.
fn 抽个动物(某数: f64) -> Box<dyn 动物> {
    if 某数 < 0.5 {
        Box::new(羊 {})
    } else {
        Box::new(牛 {})
    }
}

fn main() {
    let 某数 = 0.234;
    let 动物 = 抽个动物(某数);
    println!("You've randomly chosen an animal, and it says {}", 动物.嘶鸣());
}

如果去掉 Box<dyn>,报错如下,果然 type 和 trait 含义不同:

error[E0782]: expected a type, found a trait
  --> src/main.rs:24:21
   |
24 | fn 抽个动物(某数: f64) -> 动物 {
   |                           ^^^^
   |
help: use `impl 动物` to return an opaque type, as long as you return a single underlying type
   |
24 | fn 抽个动物(某数: f64) -> impl 动物 {
   |                           ++++
help: alternatively, you can return an owned trait object
   |
24 | fn 抽个动物(某数: f64) -> Box<dyn 动物> {
   |                           +++++++     +

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

如果用第一个建议 -> impl 动物,则提示函数体不同分支的返回类型(type)不同:

error[E0308]: `if` and `else` have incompatible types
  --> src/main.rs:28:9
   |
25 | /     if 某数 < 0.5 {
26 | |         Box::new(羊 {})
   | |         --------------- expected because of this
27 | |     } else {
28 | |         Box::new(牛 {})
   | |         ^^^^^^^^^^^^^^^ expected `Box<羊>`, found `Box<牛>`
29 | |     }
   | |_____- `if` and `else` have incompatible types
   |
   = note: expected struct `Box<羊>`
              found struct `Box<牛>`

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

有点意外的是,此次报错的建议里没有提到用 dyn。照理说可以分析出 羊 和 牛 实现了同样的 trait,就可以像上面那样提示用 dyn,不过“owned trait object”不知啥意思.

另外,impl xx for xxx 挺接近自然语法,不知自然语言里这么常用的 for 还用于其他语法不。

再说缩写,看 impl、fn 似乎是想将关键词长度控制在六个字母(如struct)以内,但 str 有必要么?随便点点其他章节,结果发现 这里:

fn university(&self) -> String;

难道 str 不是 String 吗?!

回头再看一下代码,-> &'static str 重复了三次,挺扎眼。

于是把这里的一处 ->... 去掉试试:

impl 动物 for 羊 {
    fn 嘶鸣(&self) -> &'static str {
        "baaaaah!"
    }
}

报错如下:

error[E0053]: method `嘶鸣` has an incompatible type for trait
  --> src/main.rs:11:17
   |
11 |     fn 嘶鸣(&self) {
   |                   ^ expected `&'static str`, found `()`
   |
note: type in trait
  --> src/main.rs:6:21
   |
6  |     fn 嘶鸣(&self) -> &'static str;
   |                       ^^^^^^^^^^^^
   = note: expected signature `fn(&羊) -> &'static str`
              found signature `fn(&羊) -> ()`
help: change the output type to match the trait
   |
11 |     fn 嘶鸣(&self) -> &'static str {
   |                    +++++++++++++++

error[E0308]: mismatched types
  --> src/main.rs:12:9
   |
11 |     fn 嘶鸣(&self) {
   |                   - expected `()` because of default return type
12 |         "baaaaah!"
   |         ^^^^^^^^^^ expected `()`, found `&str`

Some errors have detailed explanations: E0053, E0308.
For more information about an error, try `rustc --explain E0053`.

如果 impl 动物 for 羊 时,里面实现的 嘶鸣 必须和 动物 的同名方法有相同签名的话,为啥不能省略掉呢?

从 “expected (), found &str” 看起来 "baaaaah!" 的返回类型是 &str,那么如果把 -> &'static str 都改成 &str,应该没问题吧。

运行的确如初。看来 'static 就是强调返回值为静态。

如果把 -> 全都去掉的话,除了上面的第二个,还有显示方法必须要匹配返回类型:

error[E0277]: `()` doesn't implement `std::fmt::Display`
  --> src/main.rs:35:66
   |
35 |     println!("You've randomly chosen an animal, and it says {}", 动物.嘶鸣());
   |                                                                  ^^^^^^^^^^^ `()` cannot be formatted with the default formatter
   |
   = help: the trait `std::fmt::Display` is not implemented for `()`
   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

Some errors have detailed explanations: E0277, E0308.
For more information about an error, try `rustc --explain E0277`.

话说这个 () 算是个啥类型?术语体系里有个命名吗?

最后试了把 -> Box<dyn 动物> 去掉,报错如下:

error[E0308]: mismatched types
  --> src/main.rs:26:9
   |
26 |         Box::new(羊 {})
   |         ^^^^^^^^^^^^^^^ expected `()`, found `Box<羊>`
   |
   = note: expected unit type `()`
                 found struct `Box<羊>`
help: consider using a semicolon here
   |
26 |         Box::new(羊 {});
   |                        +
help: try adding a return type
   |
24 | fn 抽个动物(某数: f64) -> Box<羊> {
   |                        ++++++++++

error[E0308]: mismatched types
  --> src/main.rs:28:9
   |
28 |         Box::new(牛 {})
   |         ^^^^^^^^^^^^^^^ expected `()`, found `Box<牛>`
   |
   = note: expected unit type `()`
                 found struct `Box<牛>`
help: consider using a semicolon here
   |
28 |         Box::new(牛 {});
   |                        +
help: try adding a return type
   |
24 | fn 抽个动物(某数: f64) -> Box<牛> {
   |                        ++++++++++

error[E0599]: no method named `嘶鸣` found for unit type `()` in the current scope
  --> src/main.rs:35:69
   |
35 |     println!("You've randomly chosen an animal, and it says {}", 动物.嘶鸣());
   |                                                                       ^^^^ method not found in `()`
   |
   = help: items from traits can only be used if the trait is implemented and in scope
note: `动物` defines an item `嘶鸣`, perhaps you need to implement it
  --> src/main.rs:4:1
   |
4  | trait 动物 {
   | ^^^^^^^^^^

Some errors have detailed explanations: E0308, E0599.
For more information about an error, try `rustc --explain E0308`.
error: could not compile `playground` (bin "playground") due to 3 previous errors

整整五十行!!感觉这种报错反馈风格对于新手来说视觉和心理上冲击会比较大,尤其是非英文母语开发者。

不过发现了 () 的术语:unit type。

得记着 str 和 String 的疑问,先到这里。