macro_rules! as_variant {
( $enum:expr, $( $variants:path )|* ) => { ... };
( $enum:expr, $pattern:pat $( if $guard:expr )? => $inner:expr $(,)? ) => { ... };
( $( $variants:path )|* ) => { ... };
( $pattern:pat $( if $guard:expr )? => $inner:expr $(,)? ) => { ... };
}
Expand description
Convert the given enum value to Option
.
There are two main syntactic forms to this macro:
-
Variants: Enum expression followed by a comma and then one or more
|
-separated newtype (one-element tuple) variants paths that should be converted toSome(_)
. Any other variants of the enum will be converted toNone
.enum Result<T, E> { Ok(T), Err(E), } impl<T, E> Result<T, E> { pub fn ok(self) -> Option<T> { as_variant!(self, Self::Ok) } }
-
Match arm: Enum expression followed by a comma, then a pattern matching one or more variants of that enum and possibly capturing variables, then a fat right arrow followed by an expression that will be put inside
Some(_)
if the pattern matches. If the pattern doesn’t match,None
will be returned.enum ListenConfig { Ipv4 { addr: Ipv4Addr, port: u16 }, Ipv6 { addr: Ipv6Addr, port: u16 }, Unix { path: PathBuf }, } impl ListenConfig { fn port(&self) -> Option<u16> { as_variant!(self, Self::Ipv4 { port, .. } | Self::Ipv6 { port, .. } => *port) } fn privileged_port(&self) -> Option<u16> { as_variant!( self, // using a guard after the pattern also works: vvvvvvvvvvvvvvv Self::Ipv4 { port, .. } | Self::Ipv6 { port, .. } if *port < 1024 => *port, ) } }
The enum expression at the start can also be left out, which causes that as_variant!
invocation to expand to a closure that does the same thing. That is,
as_variant!(<variants or match arm>)
is the same as
|val| as_variant!(val, <variants or match arm>)
. This is especially useful for combinators,
for example Option::and_then
or Iterator::filter_map
:
let optional_ip_addr = Some("127.0.0.1".parse::<IpAddr>().unwrap());
let optional_ipv4_addr = optional_ip_addr.and_then(as_variant!(IpAddr::V4));
§More Examples
use std::ops::Deref;
use as_variant::as_variant;
enum Value {
Integer(i64),
String(String),
Array(Vec<Value>),
}
impl Value {
pub fn as_integer(&self) -> Option<i64> {
as_variant!(self, Self::Integer).copied()
}
pub fn as_str(&self) -> Option<&str> {
as_variant!(self, Self::String).map(Deref::deref)
}
pub fn as_array(&self) -> Option<&[Value]> {
as_variant!(self, Self::Array).map(Deref::deref)
}
pub fn into_string(self) -> Option<String> {
as_variant!(self, Self::String)
}
pub fn into_array(self) -> Option<Vec<Value>> {
as_variant!(self, Self::Array)
}
}
use as_variant::as_variant;
enum Either3<A, B, C> {
A(A),
B(B),
C(C),
}
impl<T, U> Either3<T, T, U> {
fn as_a_or_b(&self) -> Option<&T> {
as_variant!(self, Self::A | Self::B)
}
fn into_a_or_b(self) -> Option<T> {
as_variant!(self, Self::A | Self::B)
}
}
let a: Either3<_, _, &str> = Either3::A(1);
assert_eq!(a.as_a_or_b(), Some(&1));
assert_eq!(a.into_a_or_b(), Some(1));
let b: Either3<_, _, u8> = Either3::B("hello");
assert_eq!(b.as_a_or_b(), Some(&"hello"));
assert_eq!(b.into_a_or_b(), Some("hello"));
let c: Either3<char, _, _> = Either3::C('c');
assert_eq!(c.as_a_or_b(), None);
assert_eq!(c.into_a_or_b(), None);