Итак, Result<T, E>
имеет довольно аккуратный метод map_err
, который позволяет обрабатывать ошибки функциональным способом:
use std::io::Result;
use std::error::Error;
use std::string::ToString;
use std::io;
fn init() -> Result<u32> { Ok(42) }
fn do_work(_data: u32) -> Result<()> { Err(io::Error::new(io::ErrorKind::Other, "IO Error!")) }
fn handle_error<E: Error + ToString>(error: E, message: &str) -> E {
eprintln!("{}: {}", message, error.to_string());
error
}
fn main() {
let _ = init()
.map_err(|e| handle_error(e, "Init error"))
.and_then(do_work)
.map_err(|e| handle_error(e, "Work error")); // "Work error: IO error"
}
Было бы здорово иметь такой же функциональный стиль для обработки Option<T>::None
:
use std::io::Result;
use std::error::Error;
use std::string::ToString;
use std::io;
fn init_opt() -> Option<u32> { Some(42) }
fn do_work_opt(_data: u32) -> Option<()> { None }
fn handle_none(message: &str) {
eprintln!("{}", message);
}
fn main() {
let _ = init_opt()
.map_none(|| handle_none("Init error"))
.and_then(do_work_opt)
.map_none(|| handle_none("Work error")); // "Work error"
}
Но я не вижу подходящей замены для этого метода в документации Option
Это можно сделать с помощью подобного настраиваемого трейта
trait MapNone {
fn map_none(self, op: impl FnOnce() -> ()) -> Self;
}
impl<T> MapNone for Option<T> {
fn map_none(self, op: impl FnOnce() -> ()) -> Self {
if self.is_none() { op(); }
self
}
}
Но я уверен, что мне чего-то не хватает, и есть хороший способ сделать то же самое через стандартную библиотеку.
Full Playground