use std::ops::Drop;
#[derive(Debug)]
struct D(i32);
impl Drop for D {
fn drop(&mut self) {
println!("destructor for {}", self.0);
}
}
fn main() {
let _x = D(1);
println!("construct first variable");
let _x = D(2);
{_x;} // _x should destruct here
// println!("{:?}", _x);
println!("construct second variable");
// but actually destruct here
}
I expected _x destructed in the middle curly brace {_x;}, but it gets destructed in the end of main function.
And change {_x;} to {_x};, it's destructed as expected inside the curly brace.
Again I did more trial, change the curly brace to {_x; println!("{:?}", _x);}, _x again destructed inside the curly brace, even before the println! statement.
And I also noticed that {_x;} gives a warning path statement with no effect, what's that meaning? Why these behavior differs?
The lint tells you what's going on. A path (in this case) is basically name of a variable, it's a path because you could be referring to a static variable in another module. A path statement is a statement of the form e; where e is an expression that is only a path. It doesn't mean anything, the compiler just completely ignores such statements. That is what the lint is telling you. And that is why it's not moved.
Note that not all references of a variable move it. For example:
let a=Some("test".to_owned());
match a {
Some(_) => println!("Some string!"),
None => println!("No string!"),
}
// I can totally still use `a` here
If you want to explicity drop a variable, I suggest calling std::mem::drop on it.
What about {_x; println!("{:?}", _x); }? the compiler says _x is moved.
If expr is an expression, then {expr} is also an expression, doesn't a semicolon after an expression move that variable?
Oh, huh, now I'm confused too I guess this means moving and destructing are not coupled the way you think. I just tested this on nightly with MIR and then the destruction does happen in the right order.
Variables live to the end of the block they were created in, not just to end of block they were used in. You've created _x in main()'s block, so it will live to the end of main.
{_x;} doesn't move the variable, because there is nothing to move it to. It's more like reading the value and then doing nothing with it.
{_x} seems right as others have said, moved to return value of block.
{_x;}
warning: path statement with no effect
Possibly bug incorrect optimisation not evaluating the expression. (I'm not a rustc developer.)
{_x;_x;} is all that is needed to give error: use of moved value.
_x; _x; also.
https://doc.rust-lang.org/reference.html#path-expressions
lvalue mentioned here suggests to me move into the block (and subsequent end of block destruction) should be correct behaviour. (or destruction even sooner, thinking possibly same as let _ = _x; )
Can't give a reason for wanting to see written {_x;} or {_x} in code over less confusing drop(_x);
let _x = D(2);
let _x3 = D(3);
{_x;}
println!("here");
vs
let _x = D(2);
let _x3 = D(3);
_x;
println!("here");
vs
let _x = D(2);
let _x3 = D(3);
let _x = _x;
println!("here");
Destruction order from three above seems to suggest that _x; should not move.
Regarding the println! macro @tennix wrote above:
Again I did more trial, change the curly brace to {_x; println!("{:?}", _x);}, _x again destructed inside the curly brace, even before the println! statement.
Note before there. After would be reasonable, I assume.