I think in C++, the temporary lasts to the end of the complete expression. Here's a C++ example; the output shows A being created before, and destroyed after, B.
Ah, so it does. As usual, the best way to verify a behavior is to actually try it Thanks for the correction.
A similar experiment using println! reveals that B is dropped before the end of the function as well, which leaves us back with your original question. I only have idle speculation at this point, but will be interested to see what the experts have to say.
When an rvalue is used in an lvalue context, a temporary un-named lvalue is created and used instead. The lifetime of temporary values is typically the innermost enclosing statement; the tail expression of a block is considered part of the statement that encloses the block.
This doesn't seem to match what the compiler's saying in its error message rejecting the original example.
(I understand that the Rust Reference isn't authoritative, but I wasn't able to find a discussion of this in The Book, and the compiler's behavior doesn't make sense to me here.)
In C++14, the relevant chapter and verse is §12.2/3 Temporary Objects [class.temporary]:
Temporary objects are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created.
§1.9/10 Program execution [intro.execution] explains:
A full-expression is an expression that is not a subexpression of another expression.
So even in C++, the temporary should last to the end of the call.
The error message of the stable compiler is actually a bit clearer here:
rustc 1.12.0 (3191fbae9 2016-09-23)
error: borrowed value does not live long enough
--> <anon>:9:8
|
9 | B(&A);
| ^ does not live long enough
|
note: reference must be valid for the destruction scope surrounding statement at 9:4...
--> <anon>:9:5
|
9 | B(&A);
| ^^^^^^
note: ...but borrowed value is only valid for the statement at 9:4
--> <anon>:9:5
|
9 | B(&A);
| ^^^^^^
help: consider using a `let` binding to increase its lifetime
--> <anon>:9:5
|
9 | B(&A);
| ^^^^^^
error: aborting due to previous error
It seems that the drop is called in an implicit scope surrounding the actual statement, like:
fn main() {
{ // implicit scope for `drop`s
let b = { // implicit scope of the original statement
B(&A)
};
drop(&mut b);
}
}
Okay, @bluss and @talchas kindly explained this to me on IRC.
The problem is in the granularity of the drop-checking rules, which require A to strictly outlive B. From the point of view of that code, A and B have the same lifetime, as if one had written:
struct A;
struct B<'a>(&'a A);
impl<'a> Drop for B<'a> {
fn drop(&mut self) {}
}
fn main() {
let (a, b);
a = A;
b = B(&a);
}
The reason the presence of the Drop impl matters is that, when it's only references involved, it's permissible for reference and referent to have identical lifetimes: the reference can't be used unsafely. Introducing a Drop impl to the situation requires the referent to strictly outlive the reference, to ensure there is a clear order in which to run drop methods.
What I don't get is why the two temporaries have the same lifetime! Where does that constraint come from? What rule? And is the rule necessary for soundness?
There is some inconsistency (I've never looked at Rust or C++ Ref/Std deeply so I might be wrong).
In C++ a temp bound by a const ref is supposed to outlive the site of binding and the object being bound to. In rust this does not seem to be the case. Just need to bypass the lifetime check to know what's happening - so using pointers.
Consider:
struct A;
impl Drop for A { fn drop(&mut self) { println!("Drop A.") } }
struct B(*const A);
impl Drop for B { fn drop(&mut self) { println!("Drop B.") } }
fn main() {
let _ = B(&A as *const A); // B is destroyed after this expression itself.
}
struct A;
impl Drop for A { fn drop(&mut self) { println!("Drop A.") } }
struct B(*const A);
impl Drop for B { fn drop(&mut self) { println!("Drop B.") } }
fn main() {
let _b = B(&A as *const A); // _b will be dropped when scope exits main()
}
TBH, the second one is what I'd expect. A is a temporary, that only lives for the statement while B is a named variable that lives until the end of main.
The first variant is probably a special case and B is regarded as a temporary as well, because _ is not really a named variable.
Anyway, B takes a pointer (not a reference) to A, so A is not borrowed and the lifetimes of A and B are not related at all. That's why I'd say that the example is not relevant to the original problem.
I guess that all temporaries in the same expression are given the same lifetime.
Otherwise the compiler would have to determine a strict ordering between all temporaries.
I image that this would also lead to more (but different) restrictions. How would the ordering be determined? Left to right? Inside to outside? Or dynamically to determine the most "liberal" ordering?
g can call f even though f requires arguments with matching lifetimes, because the arguments are reborrowed (with a shorter, common lifetime) at the f(a, b) call site.
(Update: Actually, even if this were not the case, the reference lifetime of &A need not exactly match the lifetime of the A temporary, IIUC, so it might still work!)
(Also, it seems reasonable for temporaries without destructors to be grouped together and given the same lifetime, just like bindings for values without destructors that are declared in the same pattern.)
Here's a more direct adaptation of that example, to show that it works even if the lifetimes aren't equal:
struct A;
fn borrow_1<'a, 'b: 'a>(_: &'a A ,_: &'b A) { }
fn borrow_2<'b, 'a: 'b>(_: &'a A ,_: &'b A) { }
fn main() {
let outer = A;
{
let inner = A;
borrow_1(&outer, &inner);
borrow_2(&inner, &outer);
}
}
It works because 'b: 'a only requires that 'b be at least as long as 'a; if they're equal, that's acceptable. So when checking main, the borrow checker simply lets both 'a and 'b be the smaller of the two lifetimes (using separate instances of 'a and 'b for each of the calls, of course).