I've found myself using .expect("...")
to panic with an helpful error message in a lot of places where I don't care about recovering from an error. Example:
let p = "foo.txt";
let f = File::open(p).expect("File not found");
However, I would like to print more information by using a formatted string. This is what I could do:
let f = File::open(p).expect(&format("{} not found", p));
This has two problems:
The format
call will be evaluated eagerly.
It's unnecessarily verbose.
Ideally, I would like to write:
// pseudocode
let f = File::open(p).expect_fmt("{} not found", p);
But I suppose that's not possible without variadic generic functions and compile-time parsing of strings.
The only alternative I've found is the following:
let f = File::open(p).unwrap_or_else(|_| panic!("{} not found", p));
Which is still a little bit too verbose for my taste.
It's acceptable if the answer uses nightly Rust.
The format() method formats the specified value(s) and insert them inside the string's placeholder. The placeholder is defined using curly brackets: {}. Read more about the placeholders in the Placeholder section below. The format() method returns the formatted string.
In java, String format() method returns a formatted string using the given locale, specified format string, and arguments. We can concatenate the strings using this method and at the same time, we can format the output concatenated string.
The Java String. format() method returns the formatted string by a given locale, format, and argument. If the locale is not specified in the String. format() method, it uses the default locale by calling the Locale.
TL;DR: No.
While there's not much to be done about reducing your verbosity issues, you can avoid the memory allocation by using std::fmt::Arguments
trait ResultExt<T, E> {
fn expect_fmt<D>(self, msg: D) -> T
where
D: std::fmt::Display;
}
impl<T, E> ResultExt<T, E> for Result<T, E>
where
E: std::error::Error,
{
fn expect_fmt<D>(self, msg: D) -> T
where
D: std::fmt::Display,
{
match self {
Ok(t) => t,
Err(e) => panic!("{}: {}", msg, e),
}
}
}
use std::fs::File;
fn main() {
let p = "foo.txt";
let f = File::open(p).expect_fmt(format_args!("{} not found", p));
}
Feel free to adjust the trait bounds as you see fit.
not possible without variadic generic functions and compile-time parsing of strings
That's exactly what the format_args
macro does, but you still have to call it.
For avoiding eagerly formatting of the error message a closure may be used, toghether with a simple macro for reducing verbosity a little bit:
macro_rules! crash {
( $( $p:tt ),* ) => {
|_| panic!($($p),*);
};
}
then the statement sugarizes as:
let f = File::open(p).unwrap_or_else(crash!("{} not found", p));
No much added value with this approach.
Have you considered error-chain or failure as alternatives for your error management design?
They have a lot to offer for effective error handling.
Using a simple macro, you should anyway tweak creatively the syntax to get near your desiderata, below an error-chain
based example:
macro_rules! or {
( $( $p:tt ),* ) => {{
|| format!($($p),*)
}};
}
fn run() -> Result<()> {
use std::fs::File;
// This operation will fail
let p = "foo";
File::open(p).chain_err(or!("unable to open {} file", p))?;
Ok(())
}
Read as: invoke chain_error
method with a variadic arg list.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With