I don't understand why we need to call mem::ManuallyDrop::new(s) in the below code.
This code will work by simply declaring s as mut.
use std::mem;
unsafe {
let s = String::from("hello");
// Prevent automatically dropping the String's data
let mut s = mem::ManuallyDrop::new(s);
let ptr = s.as_mut_ptr();
let len = s.len();
let capacity = s.capacity();
let s = String::from_raw_parts(ptr, len, capacity);
assert_eq!(String::from("hello"), s);
}
ManuallyDrop is simply a wrapper that prevents the drop "method" of the thing being wrapped from being called automatically.
Why would we want this? Because sometimes I get bored and leaking memory is fun (sic).
Also, it is very essential for what you're doing in this example, although you have made it considerably harder to follow by shadowing s. Here is a cleaned up version:
let orig = String::from("hello");
// Let us make sure orig is never dropped
let mut orig = ManuallyDrop::new(orig);
// Now let us re-harvest the data of orig to make final
let final = unsafe {
String::from_raw_parts(orig.as_mut_ptr(), orig.len(), orig.capacity())
};
So here the drop method of final runs at the end of this code, and the memory for "hello" is freed.
But imagine you didn't make orig as ManuallyDrop. Then first final would go out of scope - freeing the memory for "hello". Then, orig would go out of scope, making the memory for "hello" to be freed again, since its drop was called.
And now you have a double free error! That's why you need a ManuallyDrop in this example.
Calling String::from_raw_parts creates a String but doesn't allocate
If you remove let mut s = mem::ManuallyDrop::new(s) from your code, then you will have a double-free
See the following example which tracks what happens:
struct MonitoredString(String, String);
impl Drop for MonitoredString {
fn drop(&mut self) {
println!("dropped {}", self.1);
}
}
//use std::mem;
fn main() {
// allocation for `String` "hello" here
let mut s = MonitoredString(String::from("hello"), String::from("first `s`"));
// Prevent automatically dropping the String's data
//let mut s = mem::ManuallyDrop::new(s);
let ptr = s.0.as_mut_ptr();
let len = s.0.len();
let capacity = s.0.capacity();
// but there is no allocation here, because `String::from_raw_parts` doesn't allocate
let s = MonitoredString(unsafe { String::from_raw_parts(ptr, len, capacity) }, String::from("second `s`"));
println!("comparing");
assert_eq!(String::from("hello"), s.0);
println!("compared")
// when this block ends, we deallocate TWICE, though we only have allocated ONCE
}
comparing
compared
dropped second `s`
dropped first `s`
Errors:
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 2.36s
Running `target/debug/playground`
free(): double free detected in tcache 2
timeout: the monitored command dumped core
/playground/tools/entrypoint.sh: line 11: 8 Aborted timeout --signal=KILL ${timeout} "$@"
You can see that memory for "hello" is released twice at the end, but looking at the source, we can see it's only allocated once here: String::from("hello").