[−][src]Macro structural::switch
Provides basic pattern matching for structural enums.
The basicness of switch
resides in that it can only branch based on the variant of
the structural enum.
Once a branch is taken(based solely on what the current variant is),
all the listed fields of the variant are destructured into the pattern for the field
(ie:in Foo{x:(a,b)}=>{}
,x is destructured into a
and b
).
switch
is intentionally limited,
it does not support pattern matching nested structural types,
or branching on the value of fields.
Exhaustiveness
Switch can handle both exhaustive and nonexhaustive enums.
When matching exhaustive enums,eiher all variants must be matched,
or the last branch must be the default branch (_=>....
).
When matching nonexhaustive enums,
the last branch must be the default branch (_=>....
).
The Structural
derive macro by default generates
the *_SI
nonexhaustive enum trait alias,
and the *_ESI
exhaustive enum trait alias.
The switch
macro considers enums it knows implement VariantCount
(a supertrait of *_ESI
traits) to be exhaustive,
otherwise they're considered non-exhaustive.
Syntax Examples
These examples demonstrates all the syntax of the switch
macro.
For a detailed description of the syntax look here
This demonstrates variant access modes:
use structural::{StructuralExt,Structural,fp,switch}; #[derive(Debug,Copy,Clone,Structural)] enum Enum{ Foo{a:u32}, Bar{b:u64}, Baz{c:&'static str}, Bam{ d:Option<usize> }, } let mut this=Enum::Foo{a:100}; assert_eq!( takes_exhaustive(Enum::Foo{a:100}), 0 ); assert_eq!( takes_exhaustive(Enum::Bar{b:100}), 1 ); assert_eq!( takes_exhaustive(Enum::Baz{c:"hello"}), 2 ); assert_eq!( takes_exhaustive(Enum::Bam{d:Some(400)}), 3 ); // This function takes any type bounded by `Enum_ESI`, // the exhaustive version of the trait generated for `Enum` by the `Structural` macro. fn takes_exhaustive<T>(mut this: T)->u32 where T: Enum_ESI, { // The matched expression is not moved if // it's a single identifier,and no branch takes it by value. // // To match the enum by reference only, you can use `switch{ref this; .... }`, // and not override it in switch branches. // Overriding looks like `ref mut Foo{x}=>{}`,and not overriding looks like `Foo{x}=>{}` // // To match the enum by mutable reference only, you can use `switch{ref mut this; .... }`, // and not override it in switch branches. switch!{ this; // `ref` here is the access mode, // and it means that fields are accessed as `&`references. ref Foo{a}=>{ assert_eq!(a,&100); 0 } // `ref mut` heremeans that fields are accessed as mutable`references. ref mut Bar{b}=>{ assert_eq!(b,&mut 100); 1 } // `move` here means that the variant fields are taken by value, move Baz{c}=>{ assert_eq!(c ,"hello"); 2 } // The enum is taken by value into the branch // (wrapped in a `VariantProxy<EnumType,TS!(Bam)>`), // because the default access mode in this switch is `move`, // since none was specified in the switch header. Bam=>{ assert_eq!(this.into_field(fp!(d)) ,Some(400)); 3 } // No `_=>{4}` branch is necessary,because this is an exhaustive enum. // // This would be necessary if the `Enum_SI` trait was used instead of `Enum_ESI`, // with the advantage that enums with more variants than `Enum` would be supported. } }
This demonstates that you can use access modes in the switch header, and how you can copy Copy fields in with different access modes:
use structural::{StructuralExt,Structural,fp,switch}; // The same enum as the first example let mut this=Enum::Foo{a:100}; // `ref` here sets default access for all variants, // which is overridable per-variant,destructuring fields into references by default. switch!{ ref this ; Foo=>{ // The `this` here is a `&VariantProxy<Enum,TS!(Foo)>`, // which allows accessing fields in variants as struct fields. assert_eq!( this.field_(fp!(a)), &100 ) } // You can copy `Copy` fields in `ref` branches with `&field_name` // The `ref`ness of this branch is inherited from // the `ref` at the top of the macro invocation. Bar{ &b }=>assert_eq!( b, 200 ), // You can copy `Copy` fields in `ref mut` branches with `&mut field_name` ref mut Baz{ &mut c }=>assert_eq!( c, "hello" ), // The `ref` here is redundant,since it's inherited from the switch header ref Bam{d}=>assert_eq!( d, &Some(400) )// The `,` is optional after the last switch arm. }
This demonstrates if
,if let
,and _=>
branches,
as well as using the variant proxy after the matches fields(in the Bam
branch).
use structural::{StructuralExt,Structural,fp,switch}; // The same enum as the first example let this=Enum::Baz{ c:"55" }; let text="99"; // `other = <expression>` is used here to be able to use // the `VariantProxy<Enum,TS!(VariantName)>` inside the switch branch, // to access any field of the matched variant(especially those that weren't destructured). // // If an expression is passed(and it's not just a variable), // then the `VariantProxy` would be inaccessible. let number = switch!{ other = this; // `if`s can only be used as guards on the `_` pattern, // never as a guard when matching on a variant if 2+2!=4 => unreachable!("2+2 is 4, silly!"), Baz{c} => c .parse::<u32>() .unwrap(), // The `,` is required here // `if can only be used as guards on the `_` pattern, // never as a guard when matching on a variant if let Ok(x@99)=text.parse::<u32>() => { println!("{:?} parses to {}u32",text,x); // The type of `other` is `Enum` in non-matching branches. println!("{:?}", other ); x } ref mut Bam{d} =>{ assert_eq!( d, &mut Some(9999) ); // The `other` here is a `&mut VariantProxy<Enum,TS!(Bam)>`, // you can access all the fields using it, // but only after the last use of destructured fields. assert_eq!( other.field_mut(fp!(d?)), Some(&mut 9999) ); 100 }, // The `,` is optional after `{...}` _=>101 // The `,` is optional after the last switch arm. }; assert_eq!(number,55);
Example
This gets the human-readable name of the direction the enum represents.
The enum can have any data so long as it has exactly the
same amount and name of variants as Direction4
.
use structural::{Structural,switch}; fn main(){ assert_eq!( Direction4::Left.direction4_to_str(), "left" ); assert_eq!( Direction4::Right.direction4_to_str(), "right" ); assert_eq!( GenericDirection4::Up("hi").direction4_to_str(), "up" ); assert_eq!( GenericDirection4::Down(vec![0,1,2]).direction4_to_str(), "down" ); } // This is an extension trait for all enums with variants of the same name as `Direction4` trait Direction4Ext: Direction4_ESI{ fn direction4_to_str(&self)->&'static str { // `switch!{ref self; .... }` isn't necesasry here,since no field is accessed. switch!{ self; Left=>"left", Right=>"right", Down=>"down", Up=>"up", } } } impl<This> Direction4Ext for This where This:?Sized+Direction4_ESI {} #[derive(Structural)] enum Direction4{ Left, Right, Down, Up, } #[derive(Structural)] enum GenericDirection4<T>{ Left(T), Right(T), Down(T), Up(T), }
More Examples
For more examples you can look at the ones in the docs for enums
Syntax
This uses a macro_rules-like syntax to describe the input to this macro. If you want to see a example that uses all the syntax,go to the syntax Example section.
switch!{
$switch_header:switch_header
$(
$switch_branch:switch_branch
)*
// The default branch:this is required when matching over nonexhaustive enums
$(_=> $branch_expr:branch_expr )?
}
switch_header
The switch_header
allows passing the matched expression,
declaring the default access mode for all variants,
and the name of the proxy for accessing fields of a variant.
Syntax(in the order in which the syntax is matched):
-
$($default_access:access_mode)? $proxy:identifier ;
-
$($default_access:access_mode)? $proxy:identifier = $matched_value:expression
; -
$($default_access:access_mode)? $matched_value:expression
;
$default_access
determines the default access mode for branches that don't specify it.
When it's not specified in the switch header,it is move
.
If no $matched_value
is passed,and $proxy
is specified,
the identifier of the matched enum will be reused for $proxy
.
If $matched_value
is passed,and $proxy
is not specified,
it'll be stored into an anonymous proxy variable,inaccessible inside the switch branches.
If $matched_value
is passed,and $proxy
is specified,
it is stored in the $proxy
variable by value before any pattern matching happens.
In switch arms that match on the variant of the enum,
$proxy
is a VariantProxy<TypeOfMatchedEnum, TS!(NameOfVariant)>
,
which allows accessing variant fields as struct fields.
In switch arms that don't match on the variant of the enum,
$proxy
is the type of the matched enum.
access_mode
access_mode
is the way that variant fields are accessed.
It can be any of:
-
ref
variant fields will be accessed by reference. -
ref mut
variant fields will be accessed by mutable reference. -
move
this is not usable yet for fields, it's currently only allowed in branches that don't list fields for the variant (eg:move Bar=>{}
). You can manually convert the variant into a single field by doingname_of_proxy.into_field(fp!(field_name))
inside the branch.
switch_branch
A switch_branch
is any of:
-
$($access:access_mode)? $variant:ident $fields:fields => $branch_expr:branch_expr
: Checks whether the enum is the$variant
variant,and if it is that variant, destructures the fields,and runs the$branch_expr
code with the enum variant bound (aVariantProxy<_,TS!($variant)>
) to the$proxy
variable (if it was declared). -
$(_)? if $condition:expression => $branch_expr:branch_expr
: A regular if expression,where$branch_expr
is run if$condition
evaluates totrue
. -
$(_)? if $pattern:pattern = $value:expression => $branch_expr:branch_expr
: A regular if let expression, where$branch_expr
is run if$value
matches the$pattern
pattern, with access to the variables declared inside the pattern.
fields
fields
can be any of:
-
{ $( $field:named_field_destructure ),* }
: A braced variant,with named fields, in which the fields can be bound by their names, or into an optional pattern.
Example:Foo{x}=>{}
,thex
field is bound to the x variable.
Example:Foo{x:(y,z)}=>{}
,thex
field is destructured into the y and z variables. -
( $( $pattern:pattern ),* )
: A tuple variant,in which fields don't have a name, and can be bound into a pattern. Example:Foo(x)=>{}
,the 0th field is bound to the x variable.
Example:Foo((y,z))=>{}
,the 0th field is destructured into the y and z variables. -
$proxy
(if it was declared in the switch header).
named_field_destructure
named_field_destructure
can be any of:
-
$field_name:identifier
: Accesses the field as: - A refernce if the variant is accessed byref
. - A mutable reference if the variant is accessed byref mut
.
Example:ref Foo{x}=>{}
,thex
field is bound as thex
reference.
Example:ref mut Foo{x}=>{}
,thex
field is bound as thex
mutable reference. -
& $field_name:identifier
: Copies the field from a variant accessed byref
.
Example:ref Foo{&x}=>{}
,thex
Copy field is copied into thex
variable. -
&mut $field_name:identifier
: Copies the field from a variant accessed byref mut
. -
$field_name:identifier : $pattern:pattern
: Destructures the field into an irrefutable pattern,
Example:ref Foo{x: &x } copies the x field into an x variable.
Example:ref Foo{x: (a,b) }
destructures the x field into a pair of references,a
andb
Example:ref mut Foo{x: &mut x } copies the x field into an x variable.
Example:ref mut Foo{x: (a,b) }
destructures the x field into a pair of mutable references,a
andb
branch_expr
A branch_expr
can be either:
-
$match_expr:expression ,
(the comma is necessary before any other branch): A single expression. -
{ $($anything:token)* } $(,)?
: Any tokens wrapped inside braces,with an optional comma trailing comma.