First of all, you need to know what a match
actually does, see this post of mine: How to understand the match pattern? - #4 by Yandros
Now, Rust being Rust, ownership comes into question. Imagine the following counter-example:
fn main ()
{
let opt_v = Some(vec![42]);
match opt_v {
| Some(v)
if v.into_iter().next().is_none() // <-- pattern guard
=> {
// use v?
drop(v)
},
| Some(v) => {
// use v?
drop(v)
},
| None => {},
}
}
So the issue lies mainly at the pattern guard line: if such code were to compile, then the guard would have taken and consumed ownership of v
, meaning that it would be impossible to use v
within its body.
Worse, if the check failed (but having already taken ownership of v
), then the following match
branches would be using a moved value.
That's why
A pattern guard is not allowed to move out of a variable bound by the pattern it guards.
This means that the v
within the pattern guard is more like *(&v)
1; so when v : impl !Copy
, unless you go and use & v ~ & *(&v) ~ &v
, you get a compiler error.
The fact that v
behaves as *(&v)
is described as: "v
has been bound by reference" (rather than by move).
Now, up until Rust version 1.39.0
, for technical reasons / due to a compiler limitation, this behavior of "v
is bound by reference" / v
behaves as *(&v)
was not supported. Instead, you were forced to make such "by ref" binding explicit by annotating the binding with ref
, which resulted in a bind-by-ref, even within the arm body, resulting in an unneeded and unjustified coding constraint.
So what currently can be written as:
match Some(vec![42]) {
| Some(v) if v.is_empty() => {
// owns v: Vec<i32>
drop::<Vec<_>>(v);
},
| _ => {},
}
had to be written as:
match Some(vec![42]) {
| Some(ref v) if v.is_empty() => {
// because of `ref`, `v` is a borrow: `v: &Vec<_>`.
},
| _ => {},
}
which means that code like the following one would fail to compile:
match vec![42] {
| v if true => drop(v),
| _ => {},
}
Conclusion
Since 1.39.0, we can bind-by-move a pattern variable in the body of a match arm guarded by a condition.
As @alice TL,DR-ed: now
it just works™
1 Rust seems to have made the sensible but opinionated choice of not using *(&mut v)
: Playground