guard
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). Due to implementation details, this macro has the rather awkward syntax guard!({ BODY } unless EXPR => PAT)
. Alternative syntaxes may be added in the future.
Examples
extern crate guard;
use env;
Cargo features
nightly
enables nightly tests.debug
enablestrace_macros
for debugging. Requires a nightly compiler (but not this crate'snightly
feature).
How it works
It's difficult to implement this behavior as a macro, because a let
statement must be created in the enclosing scope. Besides that, it is desirable to avoid the necessity of repeating the identifiers bound by the pattern. The strategy used here is to scan the pattern for identifiers, and use that to construct a top-level let
statement which internally uses a match
to apply the pattern. This scanning is almost possible -- see limitations #1 and #2 below.
This strategy also means that PAT
needs to be input to the macro as an unparsed sequence of token trees. There are two ways to take an unbounded sequence of token trees as input without causing ambiguity errors: put the token trees at the end (my current choice) or enclose them in brackets. The backwards invocation syntax is a result of this choice.
There are a number of subtleties in the expansion to avoid various warning and pitfalls; see the macro source for more details.
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:
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.