macro_rules! bind {
($n: ident = $e: expr, or $f: expr) => { ... };
(mut $n: ident = $e: expr, or $f: expr) => { ... };
($n: ident, or $f: expr) => { ... };
(mut $n: ident, or $f: expr) => { ... };
($n: ident = $e: expr, or $h: expr, $f: expr) => { ... };
(mut $n: ident = $e: expr, or $h: expr, $f: expr) => { ... };
($n: ident, or $h: expr, $f: expr) => { ... };
(mut $n: ident, or $h: expr, $f: expr) => { ... };
}Expand description
Binds the unwrapped value.
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.
Tests whether the value of the provided expression can be unwrapped. Creates a variable binding if the value can be unwrapped. Otherwise, executes the error handler and evaluates the execution flow control expression.
§Syntax
bind!([mut] <var-name> [= <value-expr>], or [<err-handler>,] <flow-ctl>);mut— indicator keyword to make the binding mutable.<var-name>— name of the newly created variable.<value-expr>— expression whose value is being tested to contain an unwrappable value. If not specified, the existing value of the variable<var-name>will be used to create new variable with the same name.<err-handler>— optional error handler that is called if there’s no value to unwrap, with error object passed as the only argument.<flow-ctl>— expression used to control the execution flow in a case when there’s no value to unwrap.
§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!();Omitting the <value-expr>:
let x = Some(42);
bind!(x /* = x */, or return);
assert_eq!(x, 42);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" } else { "unknown error "}.into()
}