类型推导如何在这个Docopt示例中工作?

sag*_*aga 2 type-inference rust type-deduction docopt

使用docopt库查看此代码:

const USAGE: &'static str = "...something...";

#[derive(Deserialize)]
struct Args {
    flag: bool,
}

type Result<T> = result::Result<T, Box<error::Error + Send + Sync>>;

fn main() {
    let mut args: Args = Docopt::new(USAGE)
        .and_then(|d| d.deserialize())
        .unwrap_or_else(|e| e.exit());
}
Run Code Online (Sandbox Code Playgroud)

如果你看一下等号右边的表达式,你会发现它没有在Args任何地方提到结构.编译器如何推导出该表达式的返回类型?类型信息可以在Rust中以相反的方向(从初始化目标到初始化表达式)流动吗?

Pet*_*all 6

"它是如何工作的?" Stack Overflow可能是一个太大的问题,但(与Scala和Haskell等其他语言一起)Rust的类型系统基于Hindley-Milner类型系统,虽然有许多修改和扩展.

大大简化,其想法是将每个未知类型视为变量,并将类型之间的关系定义为一系列约束,然后可以通过算法求解.在某些方面,它类似于您可能在学校代数中解决的联立方程式.


类型推断是Rust(以及扩展的Hindley-Milner家族中的其他语言)的一个特性,它在惯用代码中被普遍利用:

  • 减少类型注释的噪音
  • 通过不在多个地方硬编码类型来提高可维护性(DRY)

Rust的类型推断是强大的,正如你所说,可以双向流动.要Vec<T>用作更简单和更熟悉的示例,其中任何一个都是有效的:

let vec = Vec::new(1_i32);
let vec = Vec::<i32>::new();
let vec: Vec<i32> = Vec::new();
Run Code Online (Sandbox Code Playgroud)

甚至可以根据以后如何使用类型来推断类型:

let mut vec = Vec::new();
// later...
vec.push(1_i32);
Run Code Online (Sandbox Code Playgroud)

另一个很好的例子是根据预期的类型选择正确的字符串解析器:

let num: f32 = "100".parse().unwrap();
let num: i128 = "100".parse().unwrap();
let address: SocketAddr = "127.0.0.1:8080".parse().unwrap();
Run Code Online (Sandbox Code Playgroud)

那你原来的例子怎么样?

  1. Docopt::new返回a Result<Docopt, Error>,Result::Err<Error>如果提供的选项无法解析为参数,则返回a .此时,不知道参数是否有效,只是它们是正确形成的.
  2. 接下来,and_then有以下签名:
    pub fn and_then<U, F>(self, op: F) -> Result<U, E> 
    where
        F: FnOnce(T) -> Result<U, E>,
    
    Run Code Online (Sandbox Code Playgroud) 变量self具有类型Result<T, E>,其中TDocoptEError,从步骤1推断U仍是未知数,您提供的关闭后也|d| d.deserialize().
  3. 但是,我们知道,TDocopts,那么deserialize就是Docopts::deserialize,它有签名:
    fn deserialize<'a, 'de: 'a, D>(&'a self) -> Result<D, Error> 
    where
        D: Deserialize<'de>
    
    Run Code Online (Sandbox Code Playgroud) 变量self有类型Docopts.D仍然是未知的,但我们知道它U与步骤2中的类型相同.
  4. Result::unwrap_or_else 有签名:
    fn unwrap_or_else<F>(self, op: F) -> T 
    where
        F: FnOnce(E) -> T
    
    Run Code Online (Sandbox Code Playgroud) 变量self有类型Result<T, Error>.但是,我们知道,T是一样的U,并D从前面的步骤.
  5. 然后我们分配一个类型的变量Args,所以T从上一步开始Args,这意味着D步骤3(和U步骤2)也是如此Args.
  6. 编译器现在可以推断出,当您编写时deserialize,意味着该方法<Args as Deserialize>::deserialize是使用该#[derive(Deserialize)]属性自动派生的.