macro_rules! bind {
($($n: ident)+ = $e: expr, or $f: expr) => { ... };
($($n: ident)+ = $e: expr, or $h: expr, $f: expr) => { ... };
}Expand description
Binds expression e to a new variable n if it ‘has a value’ / ‘is ok’.
Otherwise, executes the optional error handler h and evaluates the f expression.
Provides the ability to write concise code to get the value or get goin’ in a context where
ErrorPropagationExpression (?)
is not sufficient, such as when controlling execution flow with break or continue,
or not applicable, as in function that does not return Result or Option.
The new variable name n may contain mut keyword to create a mutable binding.
The error value is passed as the only argument to the h error handler
if the latter is present. Determining whether the given expression e contains a valid
value or and error (and what are they) is done through IntoResult trait,
which is already implemented for Result and Option.
The f expression is used to control the execution flow in a case when
the eexpression contains an error, making binding impossible.
§Syntax
bind!([mut] <variable-name> = <value-expression>, or [<error-handler>,] <flow-control-expression>);§Examples
Basic usage:
// binds `x` to the value 42, does not return
bind!(x = Some(42), or return);
assert_eq!(x, 42);
// creates a mutable binding `x` to the value 42, does not return
bind!(mut x = Some(42), or return);
x += 3;
assert_eq!(x, 45);
// returns
bind!(x = None::<i32>, or return);
unreachable!();
// returns as well, why wouldn't it
bind!(mut x = None::<i32>, or return);
unreachable!();Handling error values:
let okish = Some(42).ok_or("error");
let errorish = None::<i32>.ok_or("error");
let handle_error = |err: &str| eprintln!("{err}!");
// binds `x` to the value 42, does not return
bind!(x = okish, or handle_error, return);
assert_eq!(x, 42);
'omit_handler: {
bind!(x = None::<i32>, or {
// it's possible to omit the error handler
// and perform handling in the flow control block
eprintln!("no value for x");
// you can control the execution flow however you like
break 'omit_handler
});
unreachable!();
}
// prints 'error!' and returns
bind!(x = errorish, or handle_error, return);
unreachable!();Using with a custom type:
struct NegativeIsError(i32);
// returns some external descriptor on success,
// negative number on failure
fn external_get_descriptor(must_succeed: bool) -> i32 {
// actual external call here
if must_succeed { 42 } else { -1 }
}
// successfully binds `x` to the value 42
bind!(x = NegativeIsError(external_get_descriptor(true)), or return);
assert_eq!(x, 42);
// prints 'error -1: unknown error' and returns
bind!(x = NegativeIsError(external_get_descriptor(false)), or print_error, return);
unreachable!();
// specifies how to determine whether `NegativeIsError`
// contains a valid descriptor or an error
impl IntoResult for NegativeIsError {
type Value = i32;
type Error = ExternalCallError;
fn into_result(self) -> Result<Self::Value, Self::Error> {
(self.0 >= 0)
.then_some(self.0)
.ok_or(ExternalCallError {
code: self.0,
desc: get_error_desc(self.0),
})
}
}
struct ExternalCallError {
code: i32,
desc: String,
}
fn print_error(err: ExternalCallError) {
let ExternalCallError { code, desc, .. } = err;
eprintln!("error {code}: {desc}");
}
fn get_error_desc(error_code: i32) -> String {
if error_code >= 0 {
"no_error".to_string()
} else {
"unknown error".to_string()
}
}