&'static lifetime means that it lasts for the entire duration of the program - which can be a great choice if you are sure that:
For this reason, it makes a little sense to deallocate them. But it doesn't mean that you cannot do it.
Box::leakFirst of all, you can dynamically allocate values to heap memory and intentionaly cause a leak, taking the static reference to them.
fn main() {
let s = String::from("Hello, world!");
let static_str: &'static str = Box::leak(s.into_boxed_str());
println!("{}", static_str);
// Memory for `static_str` cannot be deallocated now.
}
Note, however that the "Hello, world!" you passed to String::from as argument is not the same as string literal that resides in static data area that would be read by compiler as static variable.
Once you leak it and what if you want to change the reference? Well, You can actually do that.
#[test]
fn change_static_reference_field() {
struct MyStruct {
field: &'static str,
}
let static_str = "Hello, world!".to_string();
let mut my_struct = MyStruct {
field: Box::leak(static_str.into_boxed_str()),
};
my_struct.field = "Hello, Rust!";
assert_eq!(my_struct.field, "Hello, Rust!");
}
The thing is, where is static_str? and its value? Has it gone or dropped?
The unfortunate truth is, it's a leak. You keep doing this and it keeps taking over memory space.
But still, you can reclaim that leaked memory with Box::from_raw which is unsafe - feel scared? you should be.
Just as we lost ownership by intentionally leaking, this function works the other way around - it takes a hold of onwership by boxing it.
The following will help you understand how it works.
use std::cell::Cell;
use std::rc::Rc;
#[test]
fn reclaim_leaked_memory() {
// A shared counter to track drops
let drop_counter = Rc::new(Cell::new(0));
struct DroppableString {
content: String,
drop_counter: Rc<Cell<usize>>,
}
impl Drop for DroppableString {
fn drop(&mut self) {
// Increment the drop counter when the instance is dropped
self.drop_counter.set(self.drop_counter.get() + 1);
}
}
// Create a heap-allocated DroppableString
let tracked_string = DroppableString {
content: String::from("This is a tracked string."),
drop_counter: drop_counter.clone(),
};
let static_str = Box::leak(Box::new(tracked_string));
// Verify the content is available
assert_eq!(static_str.content, "This is a tracked string.");
assert_eq!(drop_counter.get(), 0); // Memory has not been reclaimed yet
// Reclaim the memory manually
unsafe {
// Get the pointer to the str
let ptr: *const DroppableString = static_str;
// Recreate the Box from the pointer and drop it
let _reclaimed_box: Box<DroppableString> = Box::from_raw(ptr as *mut DroppableString);
// Memory is reclaimed when `_reclaimed_box` goes out of scope
}
// Ensure the drop counter is incremented
assert_eq!(drop_counter.get(), 1); // Memory is reclaimed
}
If you don't know what you are doing, don't follow this practice.
If not handled carefully, it may lead to undefined behavior risk. That may be caused in the following scenarios:
So for example, the following attempt will error out:
#[test]
fn reclaim_leaked_memory_with_leak() {
// A shared counter to track drops
let s = "hello world";
unsafe {
let s: *const str = s;
let _ = Box::from_raw(s as *mut str);
}
}