在此Docopt示例中,类型推导如何工作?

时间:2019-02-26 11:48:00

标签: rust type-inference type-deduction docopt

使用docopt库查看以下代码:

{
    "data" : [
                {"key":"val","key":"val"},
                {"key":"val","key":"val"},
                {"key":"val","key":"val"},
                {"id":"666","name":"jnit"}
            ]
}

如果查看等号右边的表达式,您会发现它在任何地方都没有提到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()); } 结构。编译器如何推断此表达式的返回类型?类型信息能否在Rust中以相反的方向(从初始化目标到初始化表达式)流动?

1 个答案:

答案 0 :(得分:5)

“它是如何工作的?” 对于Stack Overflow来说可能是一个太大的问题,但是(以及其他语言,例如Scala和Haskell)Rust的类型系统基于the Hindley-Milner type system,尽管有许多修改和扩展。

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


类型推断是Rust(以及扩展的Hindley-Milner系列中的其他语言)的一项功能,该语言已在惯用代码中广泛用于以下用途:

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

Rust的类型推断功能强大,正如您所说,可以双向流动。要将Vec<T>用作一个更简单,更熟悉的示例,以下任何一个都是有效的:

let vec = Vec::new(1_i32);
let vec = Vec::<i32>::new();
let vec: Vec<i32> = Vec::new();

甚至可以根据以后使用类型的方式来推断类型:

let mut vec = Vec::new();
// later...
vec.push(1_i32);

另一个不错的例子是根据期望的类型选择正确的字符串解析器:

let num: f32 = "100".parse().unwrap();
let num: i128 = "100".parse().unwrap();
let address: SocketAddr = "127.0.0.1:8080".parse().unwrap();

那么您的原始示例呢?

  1. Docopt::new返回一个Result<Docopt, Error>,如果不能将提供的选项解析为参数,则返回Result::Err<Error>。在这一点上,不知道参数是否有效,只是它们的格式正确。
  2. 接下来,and_then具有以下签名:
    pub fn and_then<U, F>(self, op: F) -> Result<U, E> 
    where
        F: FnOnce(T) -> Result<U, E>,
    
    变量self具有类型Result<T, E>,其中TDocopt,而EError,是从步骤1推导出来的。U是即使您提供了闭包|d| d.deserialize(),仍然未知。
  3. 但是我们知道TDocopts,所以deserializeDocopts::deserialize,它的签名是:
    fn deserialize<'a, 'de: 'a, D>(&'a self) -> Result<D, Error> 
    where
        D: Deserialize<'de>
    
    变量self的类型为DocoptsD仍然未知,但我们知道它与步骤2中的U相同。
  4. Result::unwrap_or_else具有签名:
    fn unwrap_or_else<F>(self, op: F) -> T 
    where
        F: FnOnce(E) -> T
    
    变量self的类型为Result<T, Error>。但是我们知道T与上一步中的UD相同。
  5. 然后我们将变量分配给类型为Args的变量,因此上一步中的TArgs,这意味着步骤3中的D(和{{步骤2)中的1}}也是U
  6. 编译器现在可以推断出,当您编写Args时,意味着方法deserialize是通过<Args as Deserialize>::deserialize属性自动派生的。