Rust 不匹配的类型混淆编译器

Posted

技术标签:

【中文标题】Rust 不匹配的类型混淆编译器【英文标题】:Rust mismatched types confusing compiler 【发布时间】:2017-01-07 22:25:39 【问题描述】:

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

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>`

我已经能够使用其他方法解决这个问题,但为什么编译器会觉得这很混乱?

【问题讨论】:

删除struct 本身上的where 子句使其工作,例如限定Container::&lt;AliveCat&gt;alive_cat()。我认为struct/impl(两者都需要彼此)的循环性质意味着推理失败。 也可以完全摆脱Cat2 并在单独的impl 块中实现Container&lt;DeadCat&gt;Container&lt;AliveCat&gt;。但事实上,失败的推论令人惊讶。 谢谢!我不知道你提到的限定语法与结构声明一起使用,这解决了这个问题。 【参考方案1】:

可以说,编译器是“靠墙”的。

结构体Container&lt;Cat&gt; 必须在结构体声明中直接实现Schrodingers
struct Container<Cat>
where Container<Cat>: Schrodingers

Container&lt;C&gt; 的 impl 块以相同的 trait bound 跟随它。

最后,我们有关联的函数,例如alive_cat,它存在于Container&lt;Cat&gt;中的每个Cat,其中Container&lt;Cat&gt;: Schrodingers,但忽略类型参数Cat,返回一个Container&lt;AliveCat&gt;

然后编译器似乎错误地将参数类型Cat 推断为特定的AliveCat,尽管在该impl 块中每个Cat 总是AliveCat 不一定是真的,导致奇怪的不一致和误导性的错误信息。就编译器的功能而言,这可能与对 Rustc 的一些调整一起工作,但不一定会发生。

幸运的是,有一些简单的开发指南可以防止此问题发生:

仅在绝对必要时对结构使用特征边界。大多数情况下,这些特征界限可以添加到适用的impl 子句中。否则,结构中的任何边界都必须应用于所有后续的 impl 块,即使该边界与实现的项目并不特别相关。 将构造函数编写为返回Self的关联函数(或包含Self的东西,例如Result&lt;Self, _&gt;等)。这会迫使您为该构造函数编写一个独立的 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 
    

【讨论】:

以上是关于Rust 不匹配的类型混淆编译器的主要内容,如果未能解决你的问题,请参考以下文章

匹配Rust中的Option静态字符串文字[duplicate]

用匹配结果读取 Rust 中的行

为啥 Rust 编译器要求我限制泛型类型参数的生命周期(错误 E0309)?

Rust入坑指南:亡羊补牢

类型不匹配解决了通过引用获取参数的闭包

Rust入坑指南:齐头并进(下)