Опытным программистам Rust режет глаз тот факт, что следующая функция возвращает кортеж, а не Result<>:
fn add_student() -> (Student, bool)
Данный подход не только не является идиоматическим, но и вводит в заблуждение читателя кода. Непонятно, что подразумевает логическое значение bool. В ответ на вывод этой функции придется написать что-то сложное, как показано ниже:
// Добавление студента на курс
let (st, err) = add_student();
// Проверка наличия ошибки. В случае ошибки продолжить цикл
if !err {
continue;
}
Получается 5 строк с комментариями, объясняющими код. Это считается плохой практикой, так же как и короткие имена переменных.
Сначала проведем рефакторинг этих фрагментов. То, что было:
fn add_student() -> (Student, bool) {
// ...
let mut st = Student {
name: "".to_string(),
age: 0,
};
// ...
if student_name.len() < 3 {
// ...
return (st, false);
}
// ...
(st, true)
}
преобразуем в более идиоматический и читаемый вариант:
fn add_student() -> Result<Student, &'static str> {
// ...
if student_name.len() < 3 {
// ...
return Err("Student's name too short");
}
// ...
let age = age.parse.map_err(|_| "Cannot parse student's age")?;
Ok(Student {
name: student_name,
age
})
}
Понятно, что возврат статических строк в качестве ошибок не относится к разряду привычных практик, но вполне подойдет для данного примера.
В этом случае объявленным типом является &'static str (эквивалент Rust для идиомы типа const char* в С), чем объясняется совпадение текстов в кавычках. Оператор ? — одна из лучших функциональностей Rust. Он проверяет стоящий перед ним экземпляр Result<>. Если значение равно Err(e), возвращает результат, в противном случае продолжает работу. В старом коде встречался макрос try!().
В итоге проверка ожидаемого вывода функции выглядит так:
let student = if let Ok(student) = add_student() {
student
} else {
continue;
}
student_db.push(student.clone());
Это неидеальное условие, поскольку оно фактически исключает любую ошибку. Действуя таким образом, мы исходим из предположения допустимости такого подхода, но предусматриваем обработку перечисления Err(e) на индивидуальной основе.
Читать
@rust_code