Handling with refutable patterns in `for ... in` loops

#[derive(Debug)]
struct One {
    name: String,
}
#[derive(Debug)]
struct Two {
    number: u64,
}

#[derive(Debug)]
enum Choice {
    One(One),
    Two(Two),
}

fn main() {
    println!("hello world!");
    let one = Choice::One(One{name: "hello".to_owned()});
    let two = Choice::Two(Two{number: 45});
    let a: Vec<Choice> = vec![one, two];
    /*
    for Choice::One(One{name: ref i}) in a {
        println!("{:?}", i);
    }
    */
}

What is the best way to do pattern matching on the fields of a struct inside the enum? Can I do it with the for ... in loop? I am currently dealing with this by using if let inside the for loop.

for i in a {
    if let Choice::One(One {name: ref i}) = i {
        // ....
    }
}

An if let or match inside the loop is indeed the best approach. You can only use patterns that always match in for loops.

1 Like

Thanks, Alice. Also how can I match Box in an if let block?

Can you give us an example of what you are trying to do? You could always deference the box first.

let thing: Box<Option<u32>> = ...;

if let Some(number) = *thing {
  ...
}

I am trying to use the syn crate. There are a lot of structs nested within the enums. I have an enum of type syn:FnArg. I want to get access to the contents of the pat field of the Typed variant of FnArg.
I hope this makes sense.

What code have you tried so far?

        if let syn::FnArg::Typed(
                syn::PatType {
                    pat: syn::Pat::Ident(syn::PatIdent {ident: i, ..}),
                    ty: syn::Type::Path(syn::TypePath {path: } ), 
                    ..}
                )
                    
                    = i {/* ... */ }

How about splitting the pattern matching into multiple steps. So something like this?

use syn::{FnArg, Pat, PatIdent, PatType, Type, TypePath};

fn do_something(arg: &FnArg) {
    if let FnArg::Typed(PatType { pat, ty, .. }) = arg {
        if let Pat::Ident(PatIdent { ident, .. }) = &**pat {
            if let Type::Path(TypePath { path, .. }) = &**ty {
                println!("{} is a {:?}", ident, path);
            }
        }
    }
}

(playground)

You tend to get these complicate match statements when navigating ASTs. I've found the if_chain crate can help remove the rightward drift.

1 Like

Thanks, Michael! I will try this out!

1 Like

One issue I personally have had with the nested control structures approach is the indentation. Given that the body of a method starts at 0-indexed column 8, and that it is generally desirable to keep the width of a line of code to <= 80 characters, the indentation adds up really quickly. The problem here is that using descriptive names for bindings are generally longer than eg the Hungarian notion used in some C code bases, so it clashes really hard with multiple levels of indentation if you want to stay within those limits.

My solution for that, imperfect as it is, is exactly the opposite from what you suggest here: I use tuples and destructuring to smash multiple levels of control flow into one match expression.

1 Like

Also what is the best way to access a field of a struct enclosed by an enum? Is pattern matching the only way?

struct One {
    name: String,
}
struct Two {
    number: u64,
}
enum Choice {
    One(One),
    Two(Two),
}

For example, if I am given a variable of type Choice::One and I want to access the name field on it.

let mut a;
if let Choice::One(One {ref name}) = b { // b is of type Choice::One
   a = name;
}
// I want to use `a` here...

What is the best way to do this??

  1. The best way to get help here is not to repeat your question every hour, so that the readers don't get irritated and aren't tempted to ignore the question at all.
  2. The best way to handle the value which might mot be present is:
    • either to use it only where it's guaranteed to exist (i.e. in if let block),
    • or to wrap it in Option, so that you can do if let ... { Some(...) } else { None }, and use its methods (this might help to find something useful fast).
2 Likes

Not all of those who might be able to help are in your time zone. Most Rustaceans in the States were still asleep when you posted your initial question, and when you reposted a similar question one hour later.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.