macro_rules! switch {
( ref mut $($rem:tt)* ) => { ... };
( &mut $($rem:tt)* ) => { ... };
( ref $($rem:tt)* ) => { ... };
( & $($rem:tt)* ) => { ... };
(move $($rem:tt)* ) => { ... };
(mut $($rem:tt)* ) => { ... };
( $($rem:tt)+ ) => { ... };
}Expand description
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:
-
refvariant fields will be accessed by reference. -
ref mutvariant fields will be accessed by mutable reference. -
movethis 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$variantvariant,and if it is that variant, destructures the fields,and runs the$branch_exprcode with the enum variant bound (aVariantProxy<_,TS!($variant)>) to the$proxyvariable (if it was declared). -
$(_)? if $condition:expression => $branch_expr:branch_expr: A regular if expression,where$branch_expris run if$conditionevaluates totrue. -
$(_)? if $pattern:pattern = $value:expression => $branch_expr:branch_expr: A regular if let expression, where$branch_expris run if$valuematches the$patternpattern, 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}=>{},thexfield is bound to the x variable.
Example:Foo{x:(y,z)}=>{},thexfield 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. -
: A unit variant,used for querying the variant of the enum. The fields of the variants (if any) can access through the$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}=>{},thexfield is bound as thexreference.
Example:ref mut Foo{x}=>{},thexfield is bound as thexmutable reference. -
& $field_name:identifier: Copies the field from a variant accessed byref.
Example:ref Foo{&x}=>{},thexCopy field is copied into thexvariable. -
&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,aandb
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,aandb
§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.