The use case is basically exactly postfix .unwrap_or!()
.
The code that I was working on when I thought about it looks like
loop {
match input.find('\\') {
Some(pos) => {
buf.push_str(&input[..pos]);
let ch;
(ch, input) = unescape_char(&input[pos..]).unwrap();
buf.push(ch);
},
None => {
buf.push_str(input);
break;
},
};
}
unescape_char
currently returns Option
, and unwrap
here is going to panic on ill-formed input.
With assign else, this could be written
loop {
match input.find('\\') {
Some(pos) => {
buf.push_str(&input[..pos]);
let ch;
Some((ch, input)) = unescape_char(&input[pos..]) else {
return Err(Error::Unknown);
}
buf.push(ch);
},
None => {
buf.push_str(input);
break;
},
};
}
whereas without, I'd probably write it as
loop {
match input.find('\\') {
Some(pos) => {
buf.push_str(&input[..pos]);
match unescape_char(&input[pos..]) {
None => return Err(Error::Unknown),
Some((ch, after)) => {
buf.push(ch);
input = after;
}
}
},
None => {
buf.push_str(input);
break;
},
};
}
However, this isn't actually a good example, because unescape_char
should eventually return Result
saying why the escape was invalid, and more code should be added to propagate error information so a good syntax error can be produced, anyway.
Basically, the value of "assignment else" is the benefit of let else, but just niched even further from when new bindings are acceptable/desirable to when updating the existing binding is needed.
// let else
let (some, group, of, names) = match create() {
Big::Patern { that_binds: some, interesting: AndNontrivial { group, of names } }
=> (some, group, of, names),
_ => else_diverges!(),
}
// becomes
let Big::Patern { that_binds: some, interesting: AndNontrivial { group, of names } }
= create()
else {
else_diverges!()
}
// assign else
(some, group, of, names) = match create() {
Big::Patern { that_binds: some, interesting: AndNontrivial { group, of names } }
=> (some, group, of, names),
_ => else_diverges!(),
}
// becomes
Big::Patern { that_binds: some, interesting: AndNontrivial { group, of names } }
= create()
else {
else_diverges!()
}
(This was typed in the irlo text box, excuse nonoptimal formatting.)
The example is obviously overly artificial and constructed, but that's because, in general, idiomatic higher level Rust code tends to prefer a more functional style without mut
ation, and even when mut
ation happens, it's typically via &mut self
and not via reassignment.
It's very interesting that my original case that got me thinking about this had a "new" binding in the destructuring assignment. This makes the else diverges actually important, and I posit would probably be the primary case where assignment else would be practically useful: mixed assignment and new bindings. I think it'd be rare to want to reassign the values from a pattern, and not be served by just binding the whole thing into one binding rather than many.