如果在 returned 之前将 return 值分配给变量,方法 returning dyn 将无法编译

Method returning dyn fails to compile if the return value is assigned to a variable before returned

在努力编写工厂方法数小时后,该方法 returns 包含泛型字段的结构实例(实现特征),我发现它只有在我不赋值时才能编译和运行在返回结果之前将结果传递给变量(完整工作示例请参见 post 的底部):

编译运行:

fn compiles() -> Parent<dyn IsAChild> {
    return match rand::thread_rng().gen_bool(1.0/2.0) {
        true => Parent { child: Box::new(Daughter{} ) },
        false => Parent { child: Box::new(Son{} ) },
    }
}

这不编译:

fn does_not_compile() -> Parent<dyn IsAChild> {
    return match rand::thread_rng().gen_bool(1.0/2.0) {
        true => {
            let parent = Parent { child: Box::new(Daughter {}) };
            parent
        },
        false => {
            let parent = Parent { child: Box::new(Son {}) };
            parent
        },
    }

}

does_not_compile() 函数导致以下错误:

error[E0308]: mismatched types
  --> src\main.rs:39:13
   |
39 |             parent
   |             ^^^^^^ expected trait object `dyn IsAChild`, found struct `Daughter`
   |
   = note: expected struct `Parent<(dyn IsAChild + 'static)>`
              found struct `Parent<Daughter>`

这让我完全难住了。对我来说,两者之间没有语义上的区别,只是结果在返回之前(临时)存储在变量中。这是怎么回事?

完整示例(添加 rand = "*" 到依赖项):

use rand::Rng;

trait IsAChild {}

struct Parent<T: IsAChild + ?Sized> {
    child: Box<T>
}

struct Son;
impl IsAChild for Son {}

struct Daughter;
impl IsAChild for Daughter {}

fn compiles() -> Parent<dyn IsAChild> {
    return match rand::thread_rng().gen_bool(1.0/2.0) {
        true => Parent { child: Box::new(Daughter{} ) },
        false => Parent { child: Box::new(Son{} ) },
    }
}

fn compiles_too() -> Parent<dyn IsAChild> {
    return match rand::thread_rng().gen_bool(1.0/2.0) {
        true => {
            println!("It's a girl!");
            Parent { child: Box::new(Daughter{} ) }
        },
        false => {
            println!("It's a boy!");
            Parent { child: Box::new(Son{} ) }
        },
    }
}

/*
fn does_not_compile() -> Parent<dyn IsAChild> {
    return match rand::thread_rng().gen_bool(1.0/2.0) {
        true => {
            let parent = Parent { child: Box::new(Daughter {}) };
            parent
        },
        false => {
            let parent = Parent { child: Box::new(Son {}) };
            parent
        },
    }
}
*/

fn main() {
    compiles();
}    

这里的区别是类型推断。在第一个示例中,因为表达式是 return 值,编译器查看函数签名以确定 Parent 的泛型参数,它决定是 dyn IsAChild。在第二个例子中,编译器需要推断变量的类型parent。它首先查看分配给它的表达式的类型,Parent { child: Box::new(Daughter {}) } 的类型明确地是 Parent<Daughter>,因此变量被赋予该类型而不是 Parent<dyn IsAChild>。您可以通过明确地说

来修复第二个示例
let parent: Parent<dyn IsAChild> = Parent { child: Box::new(Daughter {}) };

编译器只会在必要时将 Box 的内容强制转换为未确定大小的类型,因此这就是为什么带有变量的示例需要明确的原因。

这是因为编译器从我们对它的使用中推断出装箱的确切类型 Parent

当这直接是 match 语句的结果,即函数的结果时,此类型必须完全匹配 Parent<dyn IsAChild>.

但是如果你将它存储在局部变量中,则类型不受限制,它只是 Parent<Daughter>Parent<Son>(这里没有 dyn)。 当使用这些变量来生成函数的结果时,它们不匹配。

你可以强制变量的类型let parent: Parent<dyn IsAChild> = Parent { child: Box::new(Daughter {}), };