Rust类型不匹配令编译器感到困惑

4

我尝试使用一个实现来处理一些奇怪的类型逻辑。以下是该错误的快速重构:

trait Schrodingers {}

struct AliveCat;
impl Schrodingers for Container<AliveCat> {}
struct DeadCat;
impl Schrodingers for Container<DeadCat> {}

struct Container<Cat1>
    where Container<Cat1>: Schrodingers
{
    cat: Cat1,
}

impl<Cat2> Container<Cat2>
    where Container<Cat2>: Schrodingers
{
    fn dead_cat() -> Container<DeadCat> {
        let observed_cat = DeadCat;
        Container { cat: observed_cat }
    }

    fn alive_cat() -> Container<AliveCat> {
        let observed_cat = AliveCat;
        Container { cat: observed_cat }
    }
}

fn main() {
    let dead_cat = Container::dead_cat();
    let alive_cat = Container::alive_cat();
}

这将导致编译器错误:

error[E0308]: mismatched types
  --> src/main.rs:19:26
   |
19 |         Container { cat: observed_cat }
   |                          ^^^^^^^^^^^^ expected type parameter, found struct `DeadCat`
   |
   = note: expected type `Cat2`
   = note:    found type `DeadCat`

error[E0308]: mismatched types
  --> src/main.rs:19:9
   |
19 |         Container { cat: observed_cat }
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `DeadCat`, found type parameter
   |
   = note: expected type `Container<DeadCat>`
   = note:    found type `Container<Cat2>`

error[E0308]: mismatched types
  --> src/main.rs:24:26
   |
24 |         Container { cat: observed_cat }
   |                          ^^^^^^^^^^^^ expected type parameter, found struct `AliveCat`
   |
   = note: expected type `Cat2`
   = note:    found type `AliveCat`

error[E0308]: mismatched types
  --> src/main.rs:24:9
   |
24 |         Container { cat: observed_cat }
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `AliveCat`, found type parameter
   |
   = note: expected type `Container<AliveCat>`
   = note:    found type `Container<Cat2>`

我已经能够使用其他方法解决这个问题,但为什么编译器会感到困惑呢?


1
struct 自身上的 where 子句删除可以使其正常工作,或者在 alive_cat() 中加上限定词,例如 Container::<AliveCat>。我认为 struct/impl 的循环性质(两者都需要对方)导致了类型推断失败。 - Chris Emerson
1
一个替代方案是完全摆脱Cat2,并在单独的impl块中实现Container<DeadCat>Container<AliveCat>。但确实,推断失败令人惊讶。 - E net4
谢谢!我不知道你提到的限定语法适用于结构声明,这解决了问题。 - CallumJHays
1个回答

1

编译器可以说是“被逼到墙角”了。

  • Container<Cat> 结构体绑定在结构体声明中直接实现 Schrodingers
struct Container<Cat>
where Container<Cat>: Schrodingers
  • 一个针对Container<C>的impl块与相同的trait bound在其后面。

  • 最后,我们有了关联函数,比如alive_cat,它存在于Container<Cat>中的每个Cat,其中Container<Cat>: Schrodingers,但忽略类型参数Cat,返回Container<AliveCat>

然后编译器似乎错误地将参数类型Cat推断为特定的AliveCat,即使不是每个Cat总是AliveCat在那个impl块中,导致奇怪的不一致性和误导性错误消息。就编译器的能力而言,这可能可以通过对Rustc进行一些调整来解决,但这可能并不一定会发生。

幸运的是,有简单的开发指南可以遵循,以防止这种问题发生:

  • 仅在绝对必要时在结构体上使用trait bounds。大多数情况下,这些trait bounds可以添加到适用的impl子句中。否则,结构体中的所有约束都必须应用于所有后续的impl块,即使该约束与实现的项无关。
  • 编写构造函数作为返回Self的关联函数(或包含Self的内容,如Result<Self,_>等)。这迫使您编写独立的impl块以用于该构造函数,而不涉及任何通用类型。
impl Container<DeadCat> {
    fn dead_cat() -> Self {
        let observed_cat = DeadCat;
        Container { cat: observed_cat }
    }
}

impl Container<AliveCat> {
    fn alive_cat() -> Self {
        let observed_cat = AliveCat;
        Container { cat: observed_cat }
    }
}

另请参阅:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接