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>andunsafe:#![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, …>: nounsafe, 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 theDeferGuardFieldshelper definition;) -
nor to the encompassing
structaltogether.
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 thevalue, which means the resulting type is completey unable to offerManuallyDrop::take()-like APIs of any sort, and whatnot.
(or
enum, but for the remainder of the explanation, I will stick to talking ofstructs exclusively, since it’s simpler.) ↩
Implementations§
Source§impl<FieldTy, ContainingType: DropManually<FieldTy>> SafeManuallyDrop<FieldTy, ContainingType>
impl<FieldTy, ContainingType: DropManually<FieldTy>> SafeManuallyDrop<FieldTy, ContainingType>
Sourcepub const fn new(value: FieldTy) -> Self
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.
Sourcepub const fn into_inner_defusing_impl_Drop(self) -> FieldTy
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>
impl<FieldTy, ContainingType: DropManually<FieldTy>> Deref for SafeManuallyDrop<FieldTy, ContainingType>
Source§impl<FieldTy, ContainingType: DropManually<FieldTy>> DerefMut for SafeManuallyDrop<FieldTy, ContainingType>
impl<FieldTy, ContainingType: DropManually<FieldTy>> DerefMut for SafeManuallyDrop<FieldTy, ContainingType>
Source§fn deref_mut(&mut self) -> &mut FieldTy
fn deref_mut(&mut self) -> &mut FieldTy
Source§impl<FieldTy, ContainingType: DropManually<FieldTy>> Drop for SafeManuallyDrop<FieldTy, ContainingType>
The impl tying everything together.
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> {