[][src]Macro structural::switch

macro_rules! switch {
    ( ref mut $($rem:tt)* ) => { ... };
    ( &mut $($rem:tt)* ) => { ... };
    ( ref $($rem:tt)* ) => { ... };
    ( & $($rem:tt)* ) => { ... };
    (move $($rem:tt)* ) => { ... };
    (mut $($rem:tt)* ) => { ... };
    ( $($rem:tt)+ ) => { ... };
}

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 enum is taken by value,
        // wrapped in a `VariantProxy<T,TS!(Baz)>`,
        move Baz=>{
            assert_eq!(this.into_field(fp!(c)) ,"hello");
            2
        }

        // The enum is taken by value into the branch(wrapped in a `VariantProxy<T,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 it was just the expression,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 => other
        .into_field(fp!(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 doing name_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 (a VariantProxy<_,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 to true.

  • $(_)? 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}=>{},the x field is bound to the x variable.
    Example: Foo{x:(y,z)}=>{},the x 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.

  • : 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 by ref. - A mutable reference if the variant is accessed by ref mut.
    Example: ref Foo{x}=>{},the x field is bound as the x reference.
    Example: ref mut Foo{x}=>{},the x field is bound as the x mutable reference.

  • & $field_name:identifier: Copies the field from a variant accessed by ref.
    Example: ref Foo{&x}=>{},the x Copy field is copied into the x variable.

  • &mut $field_name:identifier: Copies the field from a variant accessed by ref 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 and b
    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 and b

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.