简体   繁体   中英

Rust Global.dealloc vs ptr::drop_in_place vs ManuallyDrop

I'm relatively new to Rust. I was working on some lock-free algorithms, and started playing around with manually managing memory, something similar to C++ new/delete. I noticed a couple different ways that do this throughout the standard library components, but I want to really understand the differences and use cases of each. Here's what it seems like to me:

ManuallyDrop<Box<T>> will prevent Box's destructor from running. I can save a raw pointer to the ManuallyDrop element, and have the actual element go out of scope (what would normally be dropped in Rust) without being dropped. I can later call ManuallyDrop::drop(&mut *ptr) to drop this value manually.

I can also dereference the ManuallyDrop<Box<T>> element, save a raw pointer to just the Box<T> , and later call std::ptr::drop_in_place(box_ptr) . This is supposed to destroy the Box itself and drop the heap-allocated T .

Looking at the ManuallyDrop::drop implementation, it looks those are literally doing the exact same thing. Since ManuallyDrop is zero cost and just stores a value in it's struct, is there any difference in the above two approaches?

I can also call std::alloc::Global.dealloc(...) , which looks like it will deallocate the memory block without calling drop. So if I call this on a pointer to Box<T> , it'll deallocate the heap pointer, but won't call drop , so T will still be lying around on the heap. I could call it on a pointer to T itself, which will remove T .

From exploring the standard library, it looks like Global.dealloc gets called in the raw_vec implementation to actually remove the heap-allocated array that Vec points to. This makes sense, since it's literally trying to remove a block of memory.

Rc has a drop implementation that looks roughly like this:

// destroy the contained object
ptr::drop_in_place(self.ptr.as_mut());

// remove the implicit "strong weak" pointer now that we've
// destroyed the contents.
self.dec_weak();

if self.weak() == 0 {
    Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref()));
}

I don't really understand why it needs both the dealloc and the drop_in_place . What does the dealloc add that the drop_in_place doesn't do?

Also, if I just save a raw pointer to a heap-allocated value by doing something like Box::new(5).into_raw() , does my pointer now control that memory allocation. As in, will it remain alive until I explicitly call ptr::drop_in_place() ?

Finally, when I was playing with all this, I ran into a strange issue. After running ManuallyDrop::drop or ptr::drop_in_place on my raw pointer, I then tried running println! on the pointer's dereferenced value. Sometimes I get a scary heap error and my test fails, which is what I would expect. Other times, it just prints the same value, as if no drops happened. I also tried running ManuallyDrop::drop multiple times on the exact same value, and same thing. Sometimes a heap error, sometimes totally fine, and the same value prints out.

What is happening here?

If you come from C++, you can think of drop_in_place as calling the destructor manually, and dealloc as calling old C free .

They serve different purposes:

  • drop_in_place just calls Drop::drop , that releases the resources held by your type.
  • dealloc frees the memory pointed to by a pointer, previously allocated with alloc .

You seem to think that drop_in_place also frees the memory, but that is not the case. I think your confusion arises because Box<T> contains a dynamically allocated object, so its Box::drop implementation does release the memory used by that object, after calling its drop_in_place , of course.

That is what you see in the Rc implementation, first it calls the drop_in_place (destructor) of the inner object, then it releases the memory.

About what happens if you call drop_in_place several times in a row... well, the function is unsafe for a reason: you most likely get Uundefined Behavior . From the docs:

...if T is not Copy , using the pointed-to value after calling drop_in_place can cause undefined behavior.

Note the can cause . I think it is perfectly possible to write a type that allows calling drop several times, but it doesn't sound like such a good idea.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM