How does the Rust compiler know if a value has been moved or not?

A simple example:

struct A; fn main() { test(2); test(1); } fn test(i: i32) { println!("test"); let a = A; if i == 2 { us(a); } println!("end"); } impl Drop for A { fn drop(&mut self) { println!("drop"); } } #[allow(unused_variables)] fn us(a: A){ println!("use"); } 

When I run it, the output is:

 test use drop end test end drop 

I understand in the case of test(2) , a moves to us(a) , so it outputs "test-use-drop-end".

However, in test(1) output is "test-end-drop", which means that the compiler knows that a not been moved.

If us(a) is called, there is no need to drop a into test(i) , it will be reset to us(a) ; and if us(a) not called, a should be discarded after println!("end") .

Since it is impossible for the compiler to know whether us(a) is called or not, how does the compiler know if a.drop() will be called or not after println!("end") ?

+8
rust
source share
1 answer

This is explained in Rustnomicon :

As with Rust 1.0, the reset flags are not actually hidden in a hidden field of any type that Drop implements.

The hidden field indicates whether the current value has been deleted or not, and if not, then it is. Thus, it is known at runtime and requires a bit of book storage.


Looking to the future, there is an RFC to remove these hidden fields .

The idea behind the RFC is to replace hidden fields with:

  • Unconditional Drop Identification (no runtime verification required)
  • Hide the hidden field on the stack in the function frame for those values ​​that are conditionally discarded

This new strategy has several advantages over the old:

  • the main advantage is that #[repr(C)] now always gives a view equivalent to C, even if the struct implements Drop
  • Another important benefit is memory preservation (NOT increasing the struct size)
  • Another small advantage is a possible slight increase in speed due to unconditional drops and better caching (from reducing the amount of memory).
+9
source share

All Articles