I work on Dart which is also adding pattern matching [1]. When we designed the syntax for guards, we also considered exactly these three choices before ultimately landing on "when" too.
Our main reasoning was:
"&&" is intuitive but it means that you can have a pattern that is immediately followed by an infix operator. That can be problematic if you ever want to make "&&" a valid pattern infix operator. And, in our case, we ended up doing exactly that, so "&&" would have been ambiguous. If you were to write:
case foo && true:
Then it could be parsed as either a pattern that matches when the value is equal to the constant "foo" and is equal to the constant "true". Or it could be parsed as a pattern that matches when the value is equal to the constant "foo" followed by a pointless guard that always succeeds.
"if" is nice because it's already a reserved word and the semantics are pretty obvious. But in Dart, an if statement always has the condition in parentheses. Those are pointless in a pattern since we already have another explicit delimiter separating the guard from the case body:
case foo if (condition):
// ^
We could say that the if condition in a guard doesn't need parentheses but if conditions elsewhere do. But that's likely just an annoying footgun where users will write them unnecessarily (but harmelessly at least) in guards and forget them in if statements and get compile errors.
If Dart didn't require parentheses around if conditions, we probably would have used "if" for guard clauses to (like Scala and Rust do).
So we tried "when" and most users and team members seem to like it. Syntax design is a human-centered process so often the right answer is just what feels right to the most people.
[1]: https://github.com/dart-lang/language/blob/master/accepted/f...