I’m not 100% sure this is the true reason. At least for if let
and while let
the temporary lifetime seems to be mainly because these statements are equivalent to match statements, especially if let PAT = EXPR { ... } else { ... }
is 100% the same as match EXPR { PAT => { ... }, _ => { ... } }
.
And in match
statements you need the temporaries because ... well ... you need to keep the value being matched against because there can be multiple arms and special treatment of the last arm would be inconsistent, and you need all the other temporaries because you don’t want all the error messages about temporaries being dropped while borrowed that you would otherwise get when matching against dereferenced mutex or refcell guards etc.
for
expressions seem to have a similar reason to match
why they need to keep temporaries. The reference presents a translation that keeps the temporaries on purpose.
So now though to my main point ... today I learned:
if EXPR { BLOCK1 } else { BLOCK2 }
is not the same as
match EXPR {
true => { BLOCK1 }
false => { BLOCK2 }
}
To expand @dhm’s playground: ⟶ like this, we see that an if
statement does drop the temporaries of the condition expression. So a proper translation would be
{
let cond = EXPR;
match cond {
true => { BLOCK1 }
false => { BLOCK2 }
}
}
It the rule was about “temporaries are dropped at the end of the statement”, this wouldn’t make any sense. So I guess the actual principle is: Temporaries are dropped at the end of the statement expression for some kinds of expressions where dropping them earlier would cause to many problems.
Now for if
expressions, the condition is always an owned bool
, so there cannot possibly be any programs not compiling because the condition’s temporaries where dropped earlier.
Honestly, my personal feeling about this is that by pretending there was a consistent and simple rule about temporaries an the end of statements/expressions, we only get less optimization without much actual gain. But it is probably hard to change anything now. The most confusing rule that we have right now, IMHO, is how blocks work. A block { STMTS; EXPR }
keeps the temporaries of EXPR
in the surrounding expression. This makes { EXPR }
similar to a call to an identity function id(EXPR)
in that it moves the expression, but it makes it behave different than loop { break EXPR }
or (|| EXPR)()
or if true { EXPR } else { unreachable!() }
or match { _ => { EXPR }}
which all drop the temporaries.
It also gives rise to a weird rule that temporaries of a final expression in a function (and probably also a closure) are dropped after the local variables are, in effect this means that
fn blah() {
STMTS...
EXPR
}
is different from
fn blah() {
STMTS...
return EXPR;
}
(proof)