I was recently writing some code that intermixes Result and Option. The intent of the code was "If anything goes wrong, at all, then please set this to the empty string.
I ended up with this… monstrosity:
let base_url = (|| {
let mut toml_file = File::open(config_path)?;
toml_file.read_to_string(&mut contents)?;
let doc = contents.parse::<toml_edit::Document>()?;
Ok((|| {
let value = doc["docs"]["base-url"].as_value()?;
let value = value.as_str()?;
Some(value.to_string())
})()
.ok_or("")?)
})()
.unwrap_or_else(|_: Box<::std::error::Error>| String::from(""));
(The rustfmt here is… yeah also not spectacular)
This uses closures to contain the ? so I don’t return from the enclosing function. Basically, the error cases here aren’t errors for the caller, so while ? looks great, and works, I don’t want to jump out of the enclosing function. I want to catch any error and give String::new.
with catch, this would be
let base_url: Result<String, Box<::std::error::Error>> = do catch {
let mut toml_file = File::open(config_path)?;
toml_file.read_to_string(&mut contents)?;
let doc = contents.parse::<toml_edit::Document>()?;
let value = doc["docs"]["base-url"].as_value()?;
let value = value.as_str()?;
Ok(value.to_string())
};
let base_url = base_url.unwrap_or(String::new());
well, this doesn’t quite work, since NoneError doesn’t implement Error. I remember reading an issue about this…
anyway I’m not gonna say it’s the best code ever, but that’s the basic idea.