Search code examples
rustunsafewgpu-rs

How to perform an unsafe drop() in rust


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.


Solution

  • 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.