Reputation: 115
How can I leak memory then ensure its clean up is the real question. The following obviously does not work?
You might be asking yourself "why would someone want to do this?" Well because the crate Im using requires a str
with static
life.
use std::mem::ManuallyDrop;
use std::mem;
fn main(){
fn string_to_static_str(s: String) -> &'static str {
Box::leak(format!("{}", s).into_boxed_str())
}
let string: &'static str = string_to_static_str(String::from("hello"));
ManuallyDrop::into_inner(ManuallyDrop::new(string));
mem::forget(string);
println!("{}", string);
}
Upvotes: 2
Views: 1601
Reputation: 8944
mem::forget
does not clean up memory. In fact, it actually does the exact opposite by telling Rust not to drop
a value. To clean up a leaked box, you will need some unsafe code.
You might be asking yourself "why would someone want to do this?" Well because the crate Im using requires a str with static life.
Honestly, unless memory could be a concern then just don't clean it up. You will need unsafe code to free it and if you clean it up at the very end of the program then you don't really gain anything from doing so. When the program ends, the OS will reclaim all of the program's memory regardless and it will probably be faster than if you dropped it first.
Sounds like you are stuck with a bad crate if it requires a &'static str
when you know it does not need that lifetime. The lifetime 'static
should only be used in cases where being 'static
is absolutely required.
Here is how you can create then free a &'static str
. I could probably use your original string_to_static_str
, however I decided to change it to use Box::into_raw
instead since Box::leak
does not give any guarantees on reclaiming the value. On the other hand, you can reclaim a box via Box::from_raw
after using Box::into_raw
.
fn to_static_str<S: AsRef<str>>(str: S) -> &'static str {
let boxed = str.as_ref().to_owned().into_boxed_str();
// Use Box::into_raw we can reclaim ownership with from_raw
unsafe { &*Box::into_raw(boxed) }
}
unsafe fn free_static_str(str: &'static str) {
// Take back ownership using unsafe code
let boxed = Box::from_raw(str as *const str as *mut str);
// Not necessary since it would get dropped anyway at the end of the function,
// but it makes the intent more explicit
std::mem::drop(boxed);
}
Here is a safer way to do this. Unlike the earlier solution, this one prevents double frees and only allows you to free a value you know was created using this approach.
use std::ptr::NonNull;
pub struct Reclaimable<T: ?Sized>(NonNull<T>);
impl<T> Reclaimable<T> {
fn new(x: T) -> Self {
Self::from_boxed(Box::new(x))
}
}
impl<T: ?Sized> Reclaimable<T> {
fn from_boxed(x: Box<T>) -> Self {
Reclaimable(NonNull::new(Box::into_raw(x)).unwrap())
}
fn as_ref<'a>(&self) -> &'a T {
unsafe { &*self.0.as_ptr() }
}
unsafe fn reclaim(self) {
unsafe {
Box::from_raw(self.0.as_ptr() as *mut T);
}
}
}
Upvotes: 5