Здесь задействованы два механизма.
Первым механизмом является оператор вопросительного знака, который возвращает Err(From::from(e))
при обнаружении Err(e)
.Если тип возвращаемого значения функции Result<T, E>
, это позволяет нам возвращать любой тип ошибки F
, который E
реализует From<F>
для.
Из документации типа failure::Error
мы можем видетьчто существует универсальная реализация From
для всех типов, реализующих failure::Fail
признак , и существует универсальная реализация Fail
для всех типов, реализующих std::error::Error
(какпока они тоже Send + Sync + 'static
).В сочетании это позволяет нам возвращать любой тип, реализующий черту failure::Fail
или черту std::error::Error
.Все типы ошибок в стандартной библиотеке реализуют черту Error
, включая std::io::Error
и std::num::ParseIntError
.
Это уже объясняет, почему код компилируется, но не объясняет, как преобразования работают внутренне.Это объясняется вторым механизмом в игре - чертой объектов.Определение (слегка отредактированное) типа Error
в ящике failure
заключается в следующем:
struct Error {
imp: ErrorImpl,
}
struct ErrorImpl {
inner: Box<Inner<dyn Fail>>,
}
struct Inner<F: ?Sized + Fail> {
backtrace: Backtrace,
failure: F,
}
Тип Inner
хранит ошибку в качестве объекта признака, который использует динамическую диспетчеризацию для вызовов методов.