1

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?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
A-B
  • 487
  • 2
  • 23

1 Answers1

7

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Why handle1 is not giving an error when the program is using a memory location which has been deallocated using a destructor? – A-B Feb 27 '19 at 21:51
  • 5
    @Ankit Because *you deliberately used `unsafe` to circumvent the safety features that would issue an error*. There's a reason `Box::from_raw` is an `unsafe fn`; its safety can't be guaranteed at compile time, so it falls on *you*, the programmer, to guarantee you're using it correctly. You're not. – trent Feb 27 '19 at 21:54
  • 2
    @Ankit That's not how it works. See also [Double Free - crash or no crash](https://stackoverflow.com/q/36682186/155423). – Shepmaster Feb 27 '19 at 21:54
  • Why are there two `drop` at the end? One is for `abc` in `handle1` and one for `obj`. Isn't both `abc` and obj are pointing to the same thing? And will it work if use `let const_obj: *const ABC = &obj;` after `handle(1)`? – A-B Feb 27 '19 at 22:06
  • 2
    @Ankit you are dropping the same value 3 times. Once each when the result of `from_raw` goes out of scope and once when the variable `obj` goes out of scope. This is another example of introduced undefined behavior. – Shepmaster Feb 27 '19 at 22:09
  • 2
    @Ankit You are not at any point putting the `ABC` into a `Box`, so it can't be correct to call `Box::from_raw`. You should only call `Box::from_raw` when you want the object to be destructed; otherwise, you can just use the raw pointer or convert it to a reference. [Here's a correct, but unidiomatic example](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=03ab4fb9e633abfdf2b9e59c32539bfc); one idiomatic way would be to use references, or perhaps [leak it](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=92c444dacf6a4e407113be3ff542246d). – trent Feb 27 '19 at 22:42