SafeManuallyDrop

Struct SafeManuallyDrop 

Source
pub struct SafeManuallyDrop<FieldTy, ContainingType = MissingSecondTypeParam>
where ContainingType: DropManually<FieldTy>,
{ /* private fields */ }
Expand description

SafeManuallyDrop<FieldTy> is the safe counterpart of ManuallyDrop<FieldTy>, and the zero-runtime-overhead counterpart of Option<FieldTy>.

§Example

  • Using ManuallyDrop<FieldTy> and unsafe:

    #![deny(unsafe_code)] // require visible `#[allow()]`s in subtle functions.
    
    use ::core::mem::ManuallyDrop;
    
    pub
    struct DeferGuard<T, F : FnOnce(T)> {
        value: ManuallyDrop<T>,
        on_drop: ManuallyDrop<F>,
    }
    
    impl<T, F : FnOnce(T)> Drop for DeferGuard<T, F> {
        fn drop(&mut self) {
            #[allow(unsafe_code)] {
                let value = unsafe { // 😰
                    ManuallyDrop::take(&mut self.value)
                };
                let on_drop = unsafe { // 😰
                    ManuallyDrop::take(&mut self.on_drop)
                };
                on_drop(value);
            }
        }
    }
    
    impl<T, F : FnOnce(T)> ::core::ops::Deref for DeferGuard<T, F> {
        type Target = T;
    
        fn deref(&self) -> &T {
            &self.value
        }
    }
    // And `DerefMut`
  • Using Option<FieldTy> and .unwrap()s everywhere…

    #![forbid(unsafe_code)]
    
    struct DeferGuardFields<T, F : FnOnce(T)> {
        value: T,
        on_drop: F,
    }
    
    pub
    struct DeferGuard<T, F : FnOnce(T)>(
        Option<DeferGuardFields<T, F>>,
    );
    
    impl<T, F : FnOnce(T)> Drop for DeferGuard<T, F> {
        fn drop(&mut self) {
            let DeferGuardFields {
                value,
                on_drop,
            } = self.0.take().unwrap(); // 🤢
            on_drop(value);
        }
    }
    
    impl<T, F : FnOnce(T)> ::core::ops::Deref for DeferGuard<T, F> {
        type Target = T;
    
        fn deref(&self) -> &T {
            &self
                .0
                .as_ref()
                .unwrap() // 🤮
                .value
        }
    }
    // And `DerefMut`
  • Using SafeManuallyDrop<FieldTy, …>: no unsafe, no .unwrap()s!

    #![forbid(unsafe_code)]
    
    use ::safe_manually_drop::{DropManually, SafeManuallyDrop};
    
    struct DeferGuardFields<T, F : FnOnce(T)> {
        value: T,
        on_drop: F,
    }
    
    pub
    struct DeferGuard<T, F : FnOnce(T)>(
     // rather than `Option<DeferGuardFields<T, F>>`,
     // or `ManuallyDrop<DeferGuardFields<T, F>>`, use:
        SafeManuallyDrop<DeferGuardFields<T, F>, Self>,
    );
    
    impl<T, F : FnOnce(T)>
        DropManually<DeferGuardFields<T, F>>
    for
        DeferGuard<T, F>
    {
        fn drop_manually(
            DeferGuardFields { value, on_drop }: DeferGuardFields<T, F>,
        )
        {
            on_drop(value);
        }
    }
    
    impl<T, F : FnOnce(T)> ::core::ops::Deref for DeferGuard<T, F> {
        type Target = T;
    
        fn deref(&self) -> &T {
            &self.0.value
        }
    }
    // And `DerefMut`

§Explanation

It manages to be non-unsafe, w.r.t. ManuallyDrop<FieldTy>, by virtue of having a significantly more restricted use case: that of being used as a struct1’s field, and merely exposing owned access to the FieldTy on drop.

Such owned access, and drop logic, is exposed and defined in the companion DropManually<FieldTy> trait.

In such an impl, you shall only have access to that FieldTy:

  • no access to sibling field types,

    (this can be trivially worked around by bundling all the necessary fields together inside the SafeManuallyDrop<_>; c.f. the example above with the DeferGuardFields helper definition;)

  • nor to the encompassing struct altogether.

The latter is kind of problematic, since the desired drop glue logic is probably strongly tied to such encompassing struct.

Hence that second generic type parameter on SafeManuallyDrop<FieldTy, ContainingType>.

As its name indicates, it is expected to be the containing/encompassing struct:

use ::safe_manually_drop::SafeManuallyDrop;

struct Example {
    //       ^
    //       +-----------------------+
    //                               |
    string: SafeManuallyDrop<String, Self>,
}

That way, this containing struct can be used as the Self/implementor type for the drop glue:

use ::safe_manually_drop::DropManually;

impl DropManually<String> for Example {
    fn drop_manually(s: String) {
        // owned access to `s` here!
        if random() {
            drop(s);
        } else {
            ::core::mem::forget(s);
        }
    }
}

§Going further

In practice, neither the API of this crate, nor that of any non-macro API for that matter, can ever hope to check, impose, nor control that the ContainingType used for a SafeManuallyDrop<FieldTy, ContainingType> do match that of the containing struct.

And, as a matter of fact, there may even be legitimate cases where you may do so on purpose.

Indeed, this extra type parameter is, at the end of the day, a mere impl DropManually “identifier” / discriminant for it to be possible for anybody to write such impls for arbitrary FieldTy types, even when the FieldTy is a fully unconstrained/blanket <T>/<F> generic type, and/or when it stems from an upstream crate, or even when wanting to repeat SafeManuallyDrop<FieldTy, …> multiple types within the same struct.

In such a case, you may want distinct drop logic for one field vs. another.

If so, then consider/notice how what that ContainingType actually is, is rather a DropImplIdentifier/DropImplDiscriminant/DropStrategy mere PhantomData-like type parameter.

Which means that in this context, you will likely want to involve dedicated phantom types for the ContainingType, FieldIdentifier pair:

use ::safe_manually_drop::prelude::*;

use some_lib::Transaction;
// where `some_lib` has the following API, say:
mod some_lib {
    pub struct Transaction(());

    // Owned `self` receivers for stronger type-level guarantees.
    impl Transaction {
        pub fn commit(self) {}
        pub fn roll_back(self) {}
    }
}

enum MyType {
    AutoCommitOnDrop {
        txn: SafeManuallyDrop<Transaction, CommitOnDropStrategy>,
    },

    AutoRollBackOnDrop {
        txn: SafeManuallyDrop<Transaction, RollBackOnDropStrategy>,
    },
}

enum CommitOnDropStrategy {}
impl DropManually<Transaction> for CommitOnDropStrategy {
    fn drop_manually(txn: Transaction) {
        txn.commit();
    }
}

enum RollBackOnDropStrategy {}
impl DropManually<Transaction> for RollBackOnDropStrategy {
    fn drop_manually(txn: Transaction) {
        txn.roll_back();
    }
}

§repr() guarantee.

This type is guaranteed to be a mere #[repr(transparent)] wrapper around its FieldTy.

§A silly, but interesting example: DIY-ing our own ManuallyDrop<T>

use ::safe_manually_drop::prelude::*;

pub
enum ForgetOnDropStrategy {}

impl<T> DropManually<T> for ForgetOnDropStrategy {
    fn drop_manually(value: T) {
        ::core::mem::forget(value);
    }
}

pub
type ManuallyDrop<T> = SafeManuallyDrop<T, ForgetOnDropStrategy>;
  • Note: do not do this in actual code, since calling forget() temporarily asserts validity of the value, which means the resulting type is completey unable to offer ManuallyDrop::take()-like APIs of any sort, and whatnot.

  1. (or enum, but for the remainder of the explanation, I will stick to talking of structs exclusively, since it’s simpler.) 

Implementations§

Source§

impl<FieldTy, ContainingType: DropManually<FieldTy>> SafeManuallyDrop<FieldTy, ContainingType>

Source

pub const fn new(value: FieldTy) -> Self

Main, const-friendly, way to construct a SafeManuallyDrop<FieldTy, _> instance.

Alternatively, there is a From<FieldTy> impl as well.

Tangentially, there shall also be Deref & DerefMut impls with Target = FieldTy.

Source

pub const fn into_inner_defusing_impl_Drop(self) -> FieldTy

The inverse / reverse operation of the Self::new() constructor: deconstructs a SafeManuallyDrop<FieldTy, …> back into a bare FieldTy type, which, by virtue of this operation, shall go back to its default drop glue (rather than the overridden one of impl DropManually<FieldTy> for … {).

Such a process is typically called defusing the (extra or special) drop glue.

Trait Implementations§

Source§

impl<FieldTy, ContainingType: DropManually<FieldTy>> Deref for SafeManuallyDrop<FieldTy, ContainingType>

Source§

type Target = FieldTy

The resulting type after dereferencing.
Source§

fn deref(&self) -> &FieldTy

Dereferences the value.
Source§

impl<FieldTy, ContainingType: DropManually<FieldTy>> DerefMut for SafeManuallyDrop<FieldTy, ContainingType>

Source§

fn deref_mut(&mut self) -> &mut FieldTy

Mutably dereferences the value.
Source§

impl<FieldTy, ContainingType: DropManually<FieldTy>> Drop for SafeManuallyDrop<FieldTy, ContainingType>

The impl tying everything together.

The main reason why an impl DropManually Just Works™, thanks to the following blanket impl:

impl<FieldTy> Drop for SafeManuallyDrop<FieldTy, …> where … : DropManually<FieldTy> {

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

impl<FieldTy, ContainingType: DropManually<FieldTy>> From<FieldTy> for SafeManuallyDrop<FieldTy, ContainingType>

Source§

fn from(field: FieldTy) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

§

impl<FieldTy, ContainingType> Freeze for SafeManuallyDrop<FieldTy, ContainingType>
where FieldTy: Freeze,

§

impl<FieldTy, ContainingType> RefUnwindSafe for SafeManuallyDrop<FieldTy, ContainingType>
where FieldTy: RefUnwindSafe,

§

impl<FieldTy, ContainingType> Send for SafeManuallyDrop<FieldTy, ContainingType>
where FieldTy: Send,

§

impl<FieldTy, ContainingType> Sync for SafeManuallyDrop<FieldTy, ContainingType>
where FieldTy: Sync,

§

impl<FieldTy, ContainingType> Unpin for SafeManuallyDrop<FieldTy, ContainingType>
where FieldTy: Unpin,

§

impl<FieldTy, ContainingType> UnwindSafe for SafeManuallyDrop<FieldTy, ContainingType>
where FieldTy: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<!> for T

Source§

fn from(t: !) -> T

Converts to this type from the input type.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<P, T> Receiver for P
where P: Deref<Target = T> + ?Sized, T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.