"Как это работает?" может быть слишком сложным вопросом для Stack Overflow, но (наряду с другими языками, такими как Scala и Haskell) система типов Rust основана на система типов Хиндли-Милнера, хотя и со многими модификациями и расширениями.
Сильно упрощая, идея состоит в том, чтобы рассматривать каждый неизвестный тип как переменную и определять отношения между типами как ряд ограничений, которые затем могут быть решены с помощью алгоритма. В некотором смысле это похоже на одновременные уравнения, которые вы, возможно, решали на уроках алгебры в школе.
Вывод типов — это функция Rust (и других языков расширенного семейства Хиндли-Милнера), которая широко используется в идиоматическом коде для:
- уменьшить шум аннотаций типов
- улучшить ремонтопригодность за счет отказа от жесткого кодирования типов в нескольких местах (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
возвращает Result<Docopt, Error>
, который будет Result::Err<Error>
, если предоставленные параметры не могут быть проанализированы как аргументы. На данный момент нет сведений о том, действительны ли аргументы, а только о том, что они правильно сформированы.
- Затем
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>
, где T
— это Docopt
, а E
— это Error
, выведенный из шага 1. U
по-прежнему неизвестен, даже после того, как вы указали замыкание |d| d.deserialize()
.
- Но мы знаем, что
T
равно Docopts
, поэтому deserialize
равно Docopts::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
имеет подпись: fn unwrap_or_else<F>(self, op: F) -> T
where
F: FnOnce(E) -> T
Переменная self
имеет тип Result<T, Error>
. Но мы знаем, что T
совпадает с U
и D
из предыдущего шага.
- Затем мы присваиваем значение переменной типа
Args
, поэтому T
из предыдущего шага равно Args
, а это означает, что D
из шага 3 (и U
из шага 2) также равно Args
.
- Теперь компилятор может сделать вывод, что, когда вы написали
deserialize
, вы имели в виду метод <Args as Deserialize>::deserialize
, который был получен автоматически с помощью атрибута #[derive(Deserialize)]
.
person
Peter Hall
schedule
26.02.2019