This has been something that comes up every now and then, but I was musing about it yet again yesterday.
box
patterns are a nightly only feature that lets you write
let b = Some(Box::new(5));
match b {
Some(box n) if n < 0 => println!("Box contains negative number {}", n),
Some(n) => println!("Box contains non-negative number {}", n),
None => println!("No box"),
}
This is very convenient for some deeply nested structures, and use it often in some parts of the compiler. But this feature in particular still has open questions around syntax and behavior. It is also stopped by how could it be generalized for other container types, like Arc
or String
.
One of the concerns that people will rightly have is around "implicit behavior": having something akin to match ergonomics for a feature that performs arbitrary logic would be rightly out of the question. This means that we can't make this as convenient as it could be, where there's no new syntax, but we can get close. Another concern is that box
is not general enough, it only works with Box
.
I first thought that some new trait with an associated type for its output could be added and be made easy to derive where the borrowed inner value could be extracted by the match pattern. But that would have some either require it to be auto applicable (hiding arbitrary execution) or some top-level method call (which means that the trait needs to be implemented by most things. If we were to do the later, then we would need the first auto-derivable trait on all types, and I do not think that's worth it.
Another alternative would be to introduce deref
or some other keyword in patterns in order to opt-into calling Deref::deref()
on the field, and let the type system figure out what the outcoming type would be, relying on match ergonomics to make this nice to use. But adding this kind of special syntax is just shifting the box
pattern to a slightly more convenient thing.
Which is how I arrived to the following thought: method calls aren't syntactically allowed in patterns, for good reason. But we could allow them in the AST, and whitelist specific methods that are for<'a> fn(&'a self) -> &'a _
. Allowing this could let us write:
match value {
E::B { owned_string.as_str(): "" } => println!("empty string"),
E::B { owned_string: s } => println!("string: {}", s),
_ => {}
}
instead of the current need to nest the match
expressions
match value {
E::B { owned_string: s } => match s.as_str() {
"" => println!("empty string"),
_ => println!("string: {}", s),
}
_ => {}
}
The method call syntax is currently not supported at all in rustc
, although I am planning to accept it syntactically purely for diagnostic purposes. But I think that having a way to do this will enable some design patterns that are more prevalent in other languages to be much more ergonomic.
Thoughts? Pointers to previous discussions in the subject (there is potentially some context I might be missing)?
Edit: note that these cases were briefly considered in the original RFC for match ergonomics.