Crate guard [−] [src]
This crate exports a macro which implements most of RFC 1303 (a "let-else" or "guard" expression as you can find in Swift).
The syntax proposed in the RFC was if !let PAT = EXPR { BODY }
or let PAT = EXPR else { BODY }
(where BODY
must diverge).
The newer syntax,
guard!(let PAT = EXPR else { BODY })
. If added to the language, this would be ambiguous (considerlet foo = if bar { baz() } else { quux() }
-- is it alet
statement or alet else
statement?), however in the context of this macro there is no ambiguity because onlylet else
statements are allowed.A variation suggested in the RFC discussion,
guard!(let PAT else { BODY } = EXPR)
, is also supported.The older syntax,
guard!({ BODY } unless EXPR => PAT)
. This should be a little faster to compile than the newer syntax, which requires an initial loop through the token stream to separate the pattern, expression and body (the normalmacro_rules!
syntax is not quite powerful enough).
Example usage:
#[macro_use] extern crate guard; // read configuration from a certain environment variable // do nothing if the variable is missing guard!(let Ok(foo) = env::var("FOO") else { return }); println!("FOO = {}", foo);
Unlike if let
, pattern guards are allowed in PAT, and, if using the new syntax, unlike match
you don't get a "cannot bind by-move" error:
guard!(let Ok(foo) if foo.starts_with("bar") = env::var("FOO") else { return });
The BODY expression is enforced to be diverging at compile time (with the naive expansion to
if let
or match
, it would be allowed to return fallback values for the bindings instead of
diverging, which would violate the semantics of the guard statement, namely that code after it
never executes if the pattern doesn't match).
Limitations
- Expressions in the pattern are not supported. This is a limitation of the current Rust
macro system -- I'd like to say "parse an identifier in this position, but if that fails try
parsing an expression" but this is is impossible; I can only test for specific
identifiers. It's easy to get around this restriction: use a pattern guard (as in
match
) instead. Empty, un-namespaced enum variants and structs cause the expansion to fail, because the macro thinks they are identifiers. It's possible to get around this as well, though an open PR is aiming to take away the easiest workaround:
a. For empty enum variants, use
Empty(..)
until #29383 lands, after that include the enum name as inEnum::Empty
. b. For unit-like structs, useEmpty(..)
until #29383 lands, after that namespace it as innamespace::Empty
, or useEmpty{}
(requires#![feature(braced_empty_structs)]
).PAT
cannot be irrefutable. This is the same behavior asif let
andmatch
, and it's useless to write a guard with an irrefutable pattern anyway (you can just uselet
), so this shouldn't be an issue. This is slightly more annoying than it could be due to limitation #1. Nonetheless, if #14252 is ever fixed, irrefutable patterns could be allowed by inserting a no-op pattern guard into the expansion.
Macros
guard! |
Match a pattern to an expression, binding identifiers in the calling scope. Diverge if the match fails. |
Enums
LetElseBodyMustDiverge |
Uninhabitable type used to make an error message |