I need to perform an unsafe drop()
in Rust.
I would like to be able to write the equivalent of:
object.set_value(&value);
drop(value);
... more stuff with object ...
even though set_value
is defined to require that value
outlive object
.
Yes, I know that this is completely unsafe and I know I "shouldn't be doing this". But I'm investigating a bug, and I need value
to be dropped and its resources released precisely at this point.
Is there anyway to either get the drop
to work, or to lie to set_value
about the lifetime of value
?
Based on a comment by @cdhowie below, I suppose a better way of asking my question would be: Suppose I know that whoever wrote set_value
made a mistake, and that it doesn't need for value
to remain alive. Is there anyway for me to tell Rust "I really know what I'm doing" without waiting for a new implementation of the library.
Perhaps I mistakenly assumed this was what "unsafe" is for.
Longer explanation of what I'm doing. In wgpu-rs, the Rust implementation of WebGPU, both
struct wgpu::RenderPass
and struct wgpu::RenderBundleEncoder
implement
trait wgpu::util::RenderEncoder
.
For many of the common operations between the two structs, RenderPass
has no restrictions on the lifetimes of the function arguments, whereas RenderBundleEncoder
requires that the arguments outlive self
. I'm trying to figure out why RenderBundleEncoder
needs to be stricter, and if this strictness can be removed.
I can try various experiments in Python, which talks to Rust via a C-interface; Python doesn't respect Rust lifetimes. But it would be easier to have an all-Rust test case.
To directly answer your updated question, you can use std::mem::transmute
to extend the lifetime for the purpose of calling set_value
:
pub fn set_value(a: &'static String) {
println!("{a}");
}
pub fn main() {
let value = "bad idea".to_owned();
// SAFETY: Only lifetime changes and we are certain that
// set_value only needs value until it returns.
set_value(unsafe { std::mem::transmute(&value) });
drop(value);
}
Here is a more complete example:
use std::marker::PhantomData;
struct Mistake<'a>(PhantomData<&'a ()>);
impl<'a> Mistake<'a> {
pub fn set_value(&mut self, a: &'a String) {
println!("{a}");
}
pub fn other_method(&self) {
println!("other");
}
}
pub fn main() {
let value = "bad idea".to_owned();
let mut mistake = Mistake(PhantomData);
// SAFETY: Only lifetime changes and we are certain that
// set_value only needs value until it returns.
mistake.set_value(unsafe { std::mem::transmute::<&'_ String, &'static String>(&value) });
drop(value);
mistake.other_method();
}
Miri doesn't complain about this code. However, as pointed out in comments, a better way to proceed is by seeing if the library still compiles with a different lifetime.