Search code examples
rustsmart-pointersunsafe

Why is the destructor not called for Box::from_raw()?


I am passing a raw pointer to two different closures and converting the raw pointer to a reference using Box::from_raw() and the program is working fine.

However, after converting the raw pointer to reference, the destructor should be called automatically as the documentation says:

This function is unsafe because improper use may lead to memory problems. For example, a double-free may occur if the function is called twice on the same raw pointer.

However, I am able to access the reference to ABC even after calling Box::from_raw() on raw pointer twice and it's working fine.

struct ABC {}

impl ABC {
    pub fn new() -> ABC {
        ABC {}
    }

    pub fn print(&self, x: u32) {
        println!("Inside handle {}", x);
    }
}

fn main() {
    let obj = ABC::new();
    let const_obj: *const ABC = &obj;

    let handle = |x| {
        let abc = unsafe { Box::from_raw(const_obj as *mut ABC) };
        abc.print(x);
    };
    handle(1);

    let handle1 = |x| {
        let abc = unsafe { Box::from_raw(const_obj as *mut ABC) };
        abc.print(x);
    };
    handle1(2);
}

Rust Playground

Why is the destructor is not called for ABC after handle and before handle1 as the description for Box::from_raw() function specifies:

Specifically, the Box destructor will call the destructor of T and free the allocated memory.

Why is Box::from_raw() working multiple times on a raw pointer?


Solution

  • TL;DR you are doing it wrong.


    converting the raw pointer to a reference

    No, you are converting it into a Box, not a reference.

    the program is working fine

    It is not. You are merely being "lucky" that memory unsafety and undefined behavior isn't triggering a crash. This is likely because your type has no actual data.

    to reference, the destructor should be called automatically

    No, when references go out of scope, the destructor is not executed.

    Why is the destructor is not called

    It is, which is one of multiple reasons your code is completely and totally broken and unsafe.

    Add code to be run during destruction:

    impl Drop for ABC {
        fn drop(&mut self) {
            println!("drop")
        }
    }
    

    And you will see it is called 3 times:

    Inside handle 1
    drop
    Inside handle 2
    drop
    drop
    

    I am able to access the reference to ABC

    Yes, which is unsafe. You are breaking the rules that you are supposed to be upholding when writing unsafe code. You've taken a raw pointer, done something to make it invalid, and are then accessing the original, now invalid variable.

    The documentation also states:

    the only valid pointer to pass to this function is the one taken from another Box via the Box::into_raw function.

    You are ignoring this aspect as well.