Rust 的一个规则就是函数的参数和返回类型在编译期间就需要知道类型具体的大小,我想这大概对于多数编程语言都是如此。

Rust impl Trait 有两个地方可以用到,一个是作为参数,另外一个是作为返回类型。

作为参数使用

对于作为函数参数使用,比如 fn foo(arg: impl someTrait) 这种类型的签名,在调用的时候,根据具体类型编译出具体类型的函数。

enum DrinkFlavor {
    Sweet,
    Bitter,
    Sour,
    Other,
}

trait Drink {
    fn flavor(&self) -> DrinkFlavor;
}

struct MilkTea {}

impl Drink for MilkTea {
    fn flavor(&self) -> DrinkFlavor {
        DrinkFlavor::Sweet
    }
}

fn flavor(d: &impl Drink) -> String {
    match d.flavor() {
        DrinkFlavor::Sweet => String::from("甜的 ^_^ "),
        _ => String::from("不好喝!")
    }
}

fn main() {
    let tea = MilkTea {};
    println!("{}", flavor(&tea));
}

上面的代码在需要将 MilkTea作为 impl Drink 调用的时候,会编译出一个函数签名为 fn flavor(d: &MilkTea)函数,并且使用这个函数替换调用的地方,这符合调用参数必须是已知大小的规则。

作为返回类型使用

如果作为返回类型使用,那么返回的类型需要已知大小,也就是返回类型固定才可以使用 impl Trait


fn foo() -> impl someTrait{
    a{}
}

同样的上面的函数在编译之后也会是fn foo() -> a这种签名。如果返回的类型不确定,那么无法通过编译检查,比如下面的代码:


fn foo() -> impl someTrait{
    if ... {
        a{}
    } else {
        b{}
    }
}

因为有可能返回类型 a 或者类型 b,不符合编译期间知道返回类型的大小的规则,所以没办法通过编译。
对于这种情况,可以使用 Box<dyn Trait> 包裹起来,在堆里面分配内存,返回一个胖指针。

对于 dyn 关键字,有空再补充。