How to set the guard for specified pattern?

Code:

pub fn handle_keys(event: KeyEvent, app: &mut App) {
    match event.code {
        KeyCode::Char('q') | KeyCode::Char('c') if KeyModifiers::CONTROL == event.modifiers => {
            app.is_running = false
        }
        _ => {}
    }
}

The if takes effect for all patterns.

Are there any way could do the same thing like the following code?

    match event.code {
        KeyCode::Char('q') | (KeyCode::Char('c') if KeyModifiers::CONTROL) == event.modifiers => {
            app.is_running = false
        }
        _ => {}
    }

I don't want to use the following code, because app.is_running is repeated.

    match event.code {
        KeyCode::Char('q') => app.is_running = false,
        KeyCode::Char('c') if KeyModifiers::CONTROL == event.modifiers => app.is_running = false,
        _ => {}
    }

The syntax as written can't work, since an if condition guard is part of the match arm, not part of the pattern. The closest equivalent I could think of would be to repeat the checks in the guard:

match event.code {
    KeyCode::Char('q') | KeyCode::Char('c')
        if event.code == KeyCode::Char('q') || event.modifiers == KeyModifiers::CONTROL =>
    {
        app.is_running = false;
    }
    _ => {}
}

If the alternative is simply to do nothing when the arm doesn't match, an early return would work (this could be written as either an if or a nested match):

match event.code {
    KeyCode::Char('q') | KeyCode::Char('c') => {
        if event.code == KeyCode::Char('c') || event.modifiers != KeyModifiers::CONTROL {
            return;
        }
        app.is_running = false;
    }
    _ => {}
}

But in general, if the body is not very long, I'd be inclined to repeat it, as in your last example.

Your first two code snippets are identical (no?), so I'm not sure what it is you're trying to achieve. Could you elaborate?

There’s unfortunately no way of applying a guard just to a part of an or pattern. In your particular code example, I would probably write something like

if matches!(event.code, KeyCode::char('q))
    || matches!(event.code, KeyCode::Char('c) if event.modifiers == KeyModifiers::CONTROL)
{
    app.is_running = false
}

You could also try to use pattern matching instead of == for the .modifiers (not 100% sure if that works though for that type)… perhaps even matching on the whole event:

match event {
    KeyEvent {
        code: KeyCode::Char('q'),
        ..
    }
    | KeyEvent {
        code: KeyCode::Char('c'),
        modifiers: KeyModifiers::CONTROL,
        ..
    } => app.is_running = false,
    _ => {}
}

hm… that turns out a bit lengthy with standard rustfmt; without it you can remove some line breaks if you prefer that

match event {
    KeyEvent { code: KeyCode::Char('q'), .. }
    | KeyEvent { code: KeyCode::Char('c'), modifiers: KeyModifiers::CONTROL, .. } => {
        app.is_running = false
    }
    _ => {}
}

You could move the character test:

    KeyCode::Char(ch) if ch == 'q' || ch == 'c' && ...
2 Likes