kecakisa
kecakisa

Reputation: 41

match Option<> with many patterns and 1 conditional

I have a match block that determines who needs to send a message

match text.to_id {
            Some(id)  => {
                if let Some(tx) = state.read().unwrap().get(&id){
                    tx.send(message.clone()).unwrap();
                } else {continue;}
            } 
            None  => {
                for (_id, send) in state.read().unwrap().iter(){
                    send.send(message.clone()).unwrap();
                }
            }
        }

but if id == 0, this should send the message to everyone the same way. if I try:

None | Some(0)=>
None | Some(id) if id == 0=>

or

Some(id) if id !=0 =>

it doesn't work or send me a error:

`Some(_)` not covered

how can use pattern None too, if id==0?

Upvotes: 0

Views: 53

Answers (1)

Masklinn
Masklinn

Reputation: 42492

Patterns are traversed top-to-bottom. So if you add alternates to the None clause (overlapping with the Some() clause) you need to move the pattern to the top:

match text.to_id {
    None | Some(0) => {
        for (_id, send) in state.read().unwrap().iter(){
            send.send(message.clone()).unwrap();
        }
    }
    Some(id)  => {
        if let Some(tx) = state.read().unwrap().get(&id){
            tx.send(message.clone()).unwrap();
        } else {continue;}
    } 
}

If you add a guard to the original Some clause, then you still need to match Some(_) in the bottom clause for Some(0) and Rust does not have refinement types so it does not understand that the complement of Some(id) if id != 0 is Some(0), which is why it's telling you that Some(_) is not covered. In that case the easiest is to just use a wildcard for the second branch

match text.to_id {
    Some(id) if id != 0 => {
        if let Some(tx) = state.read().unwrap().get(&id){
            tx.send(message.clone()).unwrap();
        } else {continue;}
    } 
    _ => {
        for (_id, send) in state.read().unwrap().iter(){
            send.send(message.clone()).unwrap();
        }
    }
}

An other alternative is to fold the Some(0) case into a None via Option::filter, such that the case does not "infect" the match:

match text.to_id.filter(|id| *id != 0) {
    Some(id) => {
        if let Some(tx) = state.read().unwrap().get(&id){
            tx.send(message.clone()).unwrap();
        } else {continue;}
    } 
    None => {
        for (_id, send) in state.read().unwrap().iter(){
            send.send(message.clone()).unwrap();
        }
    }
}

Upvotes: 4

Related Questions