为什么在变量上调用方法会阻止 Rust 推断变量的类型? [英] Why does calling a method on a variable prevent Rust from inferring the type of the variable?

查看:38
本文介绍了为什么在变量上调用方法会阻止 Rust 推断变量的类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此代码编译:

#[derive(Debug, Default)]结构体示例;impl 示例 {fn some_method(&self) {}}fn 复制()->例子 {让示例 = Default::default();//example.some_method();例子}

如果把注释行加回来,会报错:

error[E0282]:需要类型注释-->src/lib.rs:10:5|9 |让示例 = Default::default();|------- 考虑给 `example` 一个类型10 |例子.some_method();|^^^^^^^ 无法推断类型|= 注意:此时必须知道类型

为什么添加这个方法调用会导致类型推断失败?

我看过这两个问题:

从他们那里,我知道 Rust 使用了一个 (修改的) Hindley-Milner 版本.后一个问题有答案,将 Rust 的类型推断描述为方程组.另一个答案明确指出Rust 中的类型信息可以倒流".

将这些知识应用于这种情况,我们有:

  1. example 是类型 ?E
  2. ?E 必须有一个名为 some_method
  3. 的方法
  4. ?E 返回
  5. 返回类型为Example

向后工作,人们很容易看到?E 必须是Example.我能看到的和编译器能看到的差距在哪里?

解决方案

根据已知事实(见下文),它无法编译,因为:

  • 类型检查器按照函数的编写顺序
  • let example = Default::default();中,example可以是任何实现Default的东西,
  • 现场访问&方法调用需要已知类型,
  • 任何实现 Default"的东西都不是已知类型.

我用一个字段访问替换了 some_method() 并且它产生了同样的错误.

<小时>

来自 类型推断取决于排序 (#42333)::><块引用>

使用 std::path::PathBuf;酒吧结构东西{pub f1: PathBuf,}fn 垃圾()->Vec<事物>{让 mut things = Vec::new();对于 vec 中的 x![1, 2, 3] {如果 x == 2 {对于things.drain(..) {thing.f1.clone();}返回 vec![]}things.push(Thing{f1: PathBuf::from(format!("/{}", x))});}事物}fn 主(){垃圾();}

这会在 Rust 1.33.0 中产生编译器错误:

error[E0282]:需要类型注释-->src/main.rs:13:17|9 |让 mut things = Vec::new();|---------- 考虑给 `things` 一个类型...13 |thing.f1.clone();|^^^^^ 无法推断类型|= 注意:此时必须知道类型

您应该关注 eddyb(Rust 语言设计的知名成员)的以下评论团队 自 2016 年 5 月).

评论 #1:

<块引用>

这是有序类型检查器的已知限制.当推理自由流动时,thing.f1.clone() before things.push(Thing {...}) 所以当您尝试访问 f1 字段时,thing: Thing 是未知的.我们将来可能会远离这一点,但目前还没有计划.

更重要的是评论#2:

<块引用>

我的意思是类型检查器按照函数的编写顺序遍历函数.[...] 除非类型已知,否则根本不支持字段访问和方法调用.

This code compiles:

#[derive(Debug, Default)]
struct Example;

impl Example {
    fn some_method(&self) {}
}

fn reproduction() -> Example {
    let example = Default::default();
    // example.some_method();
    example
}

If the commented line is added back, it will cause an error:

error[E0282]: type annotations needed
  --> src/lib.rs:10:5
   |
9  |     let example = Default::default();
   |         ------- consider giving `example` a type
10 |     example.some_method();
   |     ^^^^^^^ cannot infer type
   |
   = note: type must be known at this point

Why does adding this method call cause type inference to fail?

I've seen these two questions:

From them, I know that Rust uses a (modified) version of Hindley-Milner. The latter question has an answer that describes Rust's type inference as a system of equations. Another answer explicitly states that "Type information in Rust can flow backwards".

Using this knowledge applied to this situation, we have:

  1. example is type ?E
  2. ?E must have a method called some_method
  3. ?E is returned
  4. The return type is Example

Working backward, it's easy for a human to see that ?E must be Example. Where is the gap between what I can see and what the compiler can see?

解决方案

Based on known facts (see below), it fails to compile because:

  • the type checker goes through the function in the order it was written,
  • in let example = Default::default();, example can be anything which implements Default,
  • field accesses & method calls require a known type,
  • "anything implementing Default" is not a known type.

I replaced some_method() with a field access and it produces same error.


From Type inference depends on ordering (#42333):

use std::path::PathBuf;

pub struct Thing {
    pub f1: PathBuf,
}

fn junk() -> Vec<Thing> {
    let mut things = Vec::new();
    for x in vec![1, 2, 3] {
        if x == 2 {
            for thing in things.drain(..) {
                thing.f1.clone();
            }
            return vec![]
        }
        things.push(Thing{f1: PathBuf::from(format!("/{}", x))});
    }   
    things  
}               

fn main() { 
    junk();
}

This produces a compiler error with Rust 1.33.0:

error[E0282]: type annotations needed
  --> src/main.rs:13:17
   |
9  |     let mut things = Vec::new();
   |         ---------- consider giving `things` a type
...
13 |                 thing.f1.clone();
   |                 ^^^^^ cannot infer type
   |
   = note: type must be known at this point

You should focus on the following comments from eddyb (a well-known member of the the Rust language design team since May, 2016).

Comment #1:

This is a known limitation of the in-order type-checker. While inference flows freely, thing.f1.clone() is checked before things.push(Thing {...}) so it isn't known that thing: Thing when you try to access the f1 field. We may in the future move away from this, but there are no immediate plans.

What's more important is comment #2:

What I mean is that the type-checker goes through the function in the order it was written. [...] Fields accesses and methods calls are simply not supported unless the type is already known.

这篇关于为什么在变量上调用方法会阻止 Rust 推断变量的类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆