使用 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();
那么你原来的例子呢?
-
Docopt::new https://docs.rs/docopt/1.0.2/docopt/struct.Docopt.html#method.new返回一个
Result<Docopt, Error>
,这将是Result::Err<Error>
如果提供的选项无法解析为参数。此时,不知道参数是否有效,只知道它们的形式正确。
- 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()
.
- 但我们知道
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 开始。
-
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
从上一步开始。
- 然后我们分配给一个类型的变量
Args
, so T
上一步的结果是Args
,这意味着D
在步骤 3 中(并且U
从步骤 2) 也可以得到Args
.
- 编译器现在可以推断出当您编写时
deserialize
你的意思是方法<Args as Deserialize>::deserialize
,这是自动导出的#[derive(Deserialize)]
属性。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)