Ответ 1
Рассмотрим определение Result
:
/// `Result` is a type that represents either success (`Ok`) or failure
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
#[must_use]
pub enum Result<T, E> {
/// Contains the success value
Ok(T),
/// Contains the error value
Err(E)
}
Дистиллированный к битам, которые имеют значение, enum Result<T, E> { Ok(T), Err(E) }
.
Это не кортеж (T, E)
; скорее, это либо T
(ОК), либо E
(ошибка).
Если вы используете кортеж (T, E)
, вы должны определить как T
, так и E
. Для вашего returns_tuple
это означало определение 0 в качестве магического значения и добавление нового варианта в перечисление MyErr
, None
. None
не является ошибкой; семантически необоснованно моделировать его таким образом. Он также затем распространяется в другие места из-за требования исчерпывающего соответствия.
Когда вы имеете дело с более сложными типами, определение фиктивного значения может быть менее осуществимым или более дорогостоящим. Как обобщение, наличие фиктивных ценностей не является хорошим планом. Это слишком вероятно, что где-то по дорожке вы на самом деле попытаетесь их использовать.
У Rust есть хорошая система типов, которая позволяет избежать таких проблем.
Мне кажется, что вы пропустили силу подбора Rust; на самом деле, единственный способ получить значение из перечисления - сопоставление шаблонов; в результате такие вещи, как Result.ok()
, Result.err()
и Option.unwrap()
, реализуются с точки зрения соответствия шаблонов.
Теперь напишите свой пример таким образом, чтобы Rust выглядел лучше.
#[derive(PartialEq, Eq, Debug)]
enum MyErr {
// Now we don't need that phoney None variant.
FailOne,
}
fn returns_result() -> Result<u8, MyErr> {
Err(MyErr::FailOne)
}
#[test]
fn test_check_return_values() {
match returns_result() {
Ok(num) => println!("result: Is OK: {}", num),
Err(MyErr::FailOne) => println!("result: Failed One"),
}
}