About heap memory (de)alloc and drop

use std::alloc::{alloc, dealloc, Layout};

struct Object {
    id: u64,
}

impl Drop for Object {
    fn drop(&mut self) {
        println!("drop id={}", self.id);
    }
}

fn main() {
    #[allow(unused_assignments)]
    let mut ptr = 0 as *mut Object;
    unsafe {
        // allocate an Object (uninitialized) on heap
        let layout = Layout::new::<Object>();
        ptr = alloc(layout) as *mut Object;
        // initialize the Object
        // PROBLEM #1: Object::drop is called with *ptr before assignment
        (*ptr) = Object { id: 1234, };
        // deallocate the Object
        // PROBLEM #2: Object::drop is not called with *ptr inside dealloc
        dealloc(ptr as *mut u8, layout);
    }
}

I want to allocate memory on heap, but find two problems as the code comments.
So, what happened?
And how to let it run as expected, that is, no drop before assignment and drop inside dealloc?

Well, assignment to a place has to drop the old value if we don't want assignments in safe code to leak memory. If you want to assign to an uninitialized place, then you are looking for ptr::write().

dealloc() only deallocates memory. It doesn't have access to type information, therefore it couldn't possibly drop the value correctly. You want drop_in_place.


By the way, is there a reason why you don't just use Box? There seems to be no advantage to this code, as compared to Box::new(Object { id: 1234 }).

2 Likes

Because I want to create a static item and without deallocate. Like

static obj: Box<Object> = Box::new(Object { id: 1234 });

You know, static need Box::new is const function, but it is not.
So, I decide to use raw pointer and unsafe code.

// (*ptr) = Object { id: 1234 };
ptr.write(Object { id: 1234 });

// ...

ptr.drop_in_place();
dealloc(ptr as *mut u8, layout);

Yeah, it works. Thank you!

Then you don't need any heap allocation. Just write

static OBJECT: Object = Object { id: 1234 };

That's a terrible idea. If you need to ask how to use unsafe, then you likely can't use it correctly. If you want to put non-const-eligible data in a static, then use once_cell::sync::Lazy.

5 Likes

Sorry for my incomplete description.
In fact, there may be some other member variables.
For example,

struct Object {
    id: u64,
    name: String, // here, you can't use above code
}

Seems that it is a powerful library to do the right thing, and I will check the source code.

You could also check out std::sync::OnceLock

1 Like

Obligatory reference to the "Obstacles" koan:

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.