rfcs/text/1860-manually-drop.md

5.3 KiB
Raw Permalink Blame History

Summary

Include the ManuallyDrop wrapper in core::mem.

Motivation

Currently Rust does not specify the order in which the destructors are run. Furthermore, this order differs depending on context. RFC issue #744 exposed the fact that the current, but unspecified behaviour is relied onto for code validity and that theres at least a few instances of such code in the wild.

While a move to stabilise and document the order of destructor evaluation would technically fix the problem described above, theres another important aspect to consider here implicitness. Consider such code:

struct FruitBox {
    peach: Peach,
    banana: Banana,
}

Does this structure depend on Peachs destructor being run before Banana for correctness? Perhaps its the other way around and it is Bananas destructor that has to run first? In the common case structures do not have any such dependencies between fields, and therefore it is easy to overlook such a dependency while changing the code above to the snippet below (e.g. so the fields are sorted by name).

struct FruitBox {
    banana: Banana,
    peach: Peach,
}

For structures with dependencies between fields it is worthwhile to have ability to explicitly annotate the dependencies somehow.

Detailed design

This RFC proposes adding the following struct as a new lang item to the core::mem (and by extension the std::mem) module. mem module is a most suitable place for such type, as the module already a place for functions very similar in purpose: drop and forget.

/// Inhibits compiler from automatically calling `T`s destructor.
#[lang = "manually_drop"]
#[unstable(feature = "manually_drop", reason = "recently added", issue = "0")]
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ManuallyDrop<T> {
    value: T,
}

impl<T> ManuallyDrop<T> {
    /// Wraps a value to be manually dropped.
    #[unstable(feature = "manually_drop", reason = "recently added", issue = "0")]
    pub fn new(value: T) -> ManuallyDrop<T> {
        ManuallyDrop { value }
    }

    /// Extracts the value from the ManuallyDrop container.
    #[unstable(feature = "manually_drop", reason = "recently added", issue = "0")]
    pub fn into_inner(slot: ManuallyDrop<T>) -> T {
        slot.value
    }

    /// Manually drops the contained value.
    ///
    /// # Unsafety
    ///
    /// This function runs the destructor of the contained value and thus makes any further action
    /// with the value within invalid. The fact that this function does not consume the wrapper
    /// does not statically prevent further reuse.
    #[unstable(feature = "manually_drop", reason = "recently added", issue = "0")]
    pub unsafe fn drop(slot: &mut ManuallyDrop<T>) {
        ptr::drop_in_place(&mut slot.value)
    }
}

impl<T> Deref for ManuallyDrop<T> {
    type Target = T;
    // ...
}

impl<T> DerefMut for ManuallyDrop<T> {
    // ...
}

The lang item will be treated specially by the compiler to not emit any drop glue for this type.

Let us apply ManuallyDrop to a somewhat expanded example from the motivation:

struct FruitBox {
    // Immediately clear theres something non-trivial going on with these fields.
    peach: ManuallyDrop<Peach>,
    melon: Melon, // Field thats independent of the other two.
    banana: ManuallyDrop<Banana>,
}

impl Drop for FruitBox {
    fn drop(&mut self) {
        unsafe {
            // Explicit ordering in which field destructors are run specified in the intuitive
            // location  the destructor of the structure containing the fields.
            // Moreover, one can now reorder fields within the struct however much they want.
            ManuallyDrop::drop(&mut self.peach);
            ManuallyDrop::drop(&mut self.banana);
        }
        // After destructor for `FruitBox` runs (this function), the destructor for Melon gets
        // invoked in the usual manner, as it is not wrapped in `ManuallyDrop`.
    }
}

It is proposed that this pattern would become idiomatic for structures where fields must be dropped in a particular order.

How We Teach This

It is expected that the functions and wrapper added as a result of this RFC would be seldom necessary.

In addition to the usual API documentation, ManuallyDrop should be mentioned in reference/nomicon/elsewhere as the solution to the desire of explicit control of the order in which the structure fields gets dropped.

Alternatives

  • Stabilise some sort of drop order and make people to write code thats hard to figure out at a glance;
  • Bikeshed colour;
  • Stabilise union and let people implement this themselves:
    • Precludes (or makes it much harder) from recommending this pattern as the idiomatic way to implement destructors with dependencies.

Unresolved questions

None known.