Have I just forgotten how borrows and moves work?
let mut v = vec![1, 2, 3]; // I have some uncopyable value
if false {
let t = v; // I might do something that consumes it
}
println!("{:?}", v); // in some condition, I know for sure that I didn't consume it
Can I somehow use an unsafe
clause to tell the compiler to trust me?
Any solution must have no runtime overhead.
Moves and copies are fundamental concepts in Rust. These might be completely new to programmers coming from garbage collected languages like Ruby, Python or C#. While these terms do exist in C++, their meaning in Rust is subtly different.
For more information on the move keyword, see the closures section of the Rust book or the threads section.
When a value is moved, Rust does a shallow copy; but what if you want to create a deep copy like in C++? To allow that, a type must first implement the Clone trait. Then to make a deep copy, client code should call the clone method: This results in the following memory layout after the clone call:
Simply hit the escape key and open the options tab! While it's now easier to edit many things, console commands still come in handy. Below is a list of use Rust console commands for both players and admins.
The compiler won't let you access a variable you may have moved the value out of, even in unsafe code.
Some workarounds:
Wrap it in an Option
. You can then move the data out using the take
method, leaving a None
value behind.
This is the approach I recommend for local variables.
Replace the original vector by an empty vector. This is cheap, since empty vectors don't allocate.
let t = std::mem::replace(&mut v, Vec::new());
This is the closest equivalent to C++ moving, which is described as:
Unless otherwise specified, such moved-from objects shall be placed in a valid but unspecified state.
Wrap it in ManuallyDrop
(this is safer than mem::forget
because it doesn't drop the value when a panic happens). Drop it manually at the end on the path where it's still initialized. Use deref to access it while it is still valid. ptr::read
to copy the value out, treating the original location as invalid/uninitialized.
This shouldn't have any runtime overhead, but I strongly recommend not using this on local variables. It's just not worth the complexity and risks.
use std::mem::ManuallyDrop;
use std::ptr;
fn main() {
let flag = //...;
unsafe {
let mut v = ManuallyDrop::new(vec![1, 2, 3]); // I have some uncopyable value
if flag {
let t = ptr::read(&*v); // I might do something that consumes it
// don't touch *v from now on
println!("{:?}", t);
}
if !flag {
println!("{:?}", *v); // in some condition, I know for sure that I didn't consume it
ManuallyDrop::drop(&mut v);
}
}
}
playground
No.
I know for sure that I didn't consume it
Just because you didn't write any code that didn't consume it doesn't mean that it wasn't consumed. Ownership and conditionally executed code discusses the mechanics of type- and stack-based drop flags further, but conceptually your code is:
let v = vec![1, 2, 3];
if false {
let _t = v;
drop(_t);
} else {
drop(v);
}
println!("{:?}", v);
Once the conditional is over, your value is as good as gone. (implementation-wise, the drops do happen at the end of the function, but the semantics don't express that).
in some condition
That condition would be the else
block of your if
statement:
if false {
let _t = v;
} else {
println!("{:?}", v);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With