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

2024-04-13

使用 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());
}

如果您查看等号右侧的表达式,您会发现它没有提到Args任何地方都可以构造。编译器如何推断该表达式的返回类型? Rust 中类型信息可以以相反的方向流动(从初始化目标到初始化表达式)吗?


“它是如何工作的?”对于 Stack Overflow 来说可能是一个太大的问题,但是(以及 Scala 和 Haskell 等其他语言)Rust 的类型系统基于Hindley-Milner 类型系统 https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_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 https://docs.rs/docopt/1.0.2/docopt/struct.Docopt.html#method.new返回一个Result<Docopt, Error>,这将是Result::Err<Error>如果提供的选项无法解析为参数。此时,不知道参数是否有效,只知道它们的形式正确。
  2. Next, and_then https://doc.rust-lang.org/std/result/enum.Result.html#method.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> where T is Docopt and E is Error,从步骤 1 推导出来。U仍然未知,即使在您提供关闭之后|d| d.deserialize().
  3. 但我们知道T is Docopts, so deserialize is Docopts::deserialize https://docs.rs/docopt/1.0.2/docopt/struct.Docopt.html#method.deserialize,其签名为:
    fn deserialize<'a, 'de: 'a, D>(&'a self) -> Result<D, Error> 
    where
        D: Deserialize<'de>
    
    变量self有类型Docopts. D仍然未知,但我们知道它与以下类型相同U从步骤 2 开始。
  4. Result::unwrap_or_else https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap_or_else有签名:
    fn unwrap_or_else<F>(self, op: F) -> T 
    where
        F: FnOnce(E) -> T
    
    变量self有类型Result<T, Error>。但我们知道T是相同的U and D从上一步开始。
  5. 然后我们分配给一个类型的变量Args, so T上一步的结果是Args,这意味着D在步骤 3 中(并且U从步骤 2) 也可以得到Args.
  6. 编译器现在可以推断出当您编写时deserialize你的意思是方法<Args as Deserialize>::deserialize,这是自动导出的#[derive(Deserialize)]属性。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在这个 Docopt 示例中类型推导是如何工作的? 的相关文章

随机推荐