pub struct MaybeDangling<T> { /* private fields */ }
Expand description

Like crate::ManuallyDrop but for having drop glue. This wrapper is 0-cost.

In other words, a MaybeDangling<T> is just like T, but for having been stripped of aliasing/dereferenceable-ity properties.

Its usage should be quite rare and advanced: if you are intending to keep hold of a potentially dangling / exhausted value, chances are you won’t want implicit/automatic drop glue of it without having previously checked for lack of exhaustion ⚠️.

That is, it is strongly advisable to be using crate::ManuallyDrop<T> instead!

Opting into unstable #[may_dangle] and the dropck_eyepatch

Ironically, for this drop glue to be as smooth as it should be, the unstable #[may_dangle] feature is needed.

But by virtue of being unstable, it cannot be offered by this crate on stable Rust.

For the adventurous nightly users, you can enable the nightly-dropck_eyepatch Cargo feature to opt into the usage of the eponymous rustc feature so as to get the Drop implementation amended accordingly.

Explanation:

Click to show
What does it mean to have a “dangling T”?

Note that the terminology of a “dangling T” can be a bit hard to visualize. The idea is to consider some 'dangling lifetime (e.g., some &'dangling borrow), to then imagine different type definitions involving it.

For instance:

  1. T = &'dangling str
  2. T = PrintOnDrop<&'dangling str>,
  3. T = Box<&'dangling str>,
  4. T = (u8, Box<PrintOnDrop<&'dangling str>>),
  5. T = &mut PrintOnDrop<&'dangling str>,

The key idea is that there are three categories of types here:

  1. The types with no drop glue at all, i.e., types for which mem::needs_drop::<T>() returns false: types 1. and 5.

    Such types should be allowed to go out of scope at a point where their lifetime may be 'dangling.

  2. The types with drop glue known not to involve a dereference of the &'dangling reference: type 3.

    Such types can be allowed to go out of scope (and thus, run their drop glue) at a point where their lifetime may be 'dangling.

  3. The types with drop glue (potentially) involving a dereference of the &'dangling reference: types 2. and 4.

    Such types should never be allowed to go out of scope at a point where their lifetime may be 'dangling.

Notice how a useful distinction thus revolves around the presence of drop glue or lack thereof, to determine whether we are in the first category, or the other two. On the other hand, whether a type directly implements Drop, such as Box or PrintOnDrop, or does not (wrapper types containing it, such as String w.r.t. the Drop implementation of the underlying Vec<u8>, or (u8, Box<...>) in the fourth example type above), is not enough information to distinguish between the two, as

  • types 2. and 3. both implement Drop, and yet belong to different categories,

  • type 4. does not implement Drop, and yet belongs to the same category as type 2.

See the drop_bounds lint for more info.

The distinction between the second and third category is whether a generic type, when dropped,

  1. merely drops its inner T (like Box<T> does) and

  2. makes it known to the drop checker that it does so.

If a type violates either restriction, either by unconditionally using any other API of T, like PrintOnDrop<T: Debug> does, or by not making it known to the drop checker that it merely drops its inner T, it will belong to category 3, which can’t be allowed to compile.

Making it known to the drop checker that T is merely dropped requires the unstable #[may_dangle] attribute. The drop checker does not know the implementation details of any Drop implementation. It can’t statically analyse how T is used in the destructor. Instead, drop check requires every generic argument to strictly outlive the wrapper type to guarantee soundness. This can be overly restrictive when merely dropping T, making it impossible to have Drop implementations where T might be dangling, even if dropping a dangling T would be sound in the given context. Hence the #[may_dangle] attribute is required to manually and unsafely tell drop check that T is merely dropped in the generic type’s destructor, relaxing the drop checker in situations where its soundness requirements are overly restrictive. With the nightly-dropck_eyepatch feature enabled, MaybeDangling<T> uses #[may_dangle] under the hood to let drop check know that it won’t access the potentially dangling T (e.g., the str behind T = &'dangling str) in its destructor, unless T’s 'dangling lifetime is involved in transitive drop glue, i.e.:

  • whenever T implements Drop (without #[may_dangle]);
  • or whenever T transitively owns some field with drop glue involving 'dangling.

With that context in mind, let’s look at examples for the three categories:

Category 1: T has no drop glue (e.g., T = &'dangling str)

Since T does not have drop glue (mem::needs_drop::<T>() returns false), the drop checker will allow this to compile, even though the reference dangles when v gets dropped:

use ::maybe_dangling::MaybeDangling;

fn main() {
    let s: String = "I will dangle".into();
    let v = MaybeDangling::new(&s);
    drop(s); // <- makes `&s` dangle
} // <- `v` dropped here, despite containing a `&'dangling s` reference!
Category 2: T has drop glue known not to involve 'dangling (e.g., T = Box<&'dangling str>)

Now that T is has drop glue, it must be executed when v is dropped. Box<&'dangling str>’s Drop implementation is known not to involve 'dangling, so it is safe for &'dangling str to dangle when the Box is dropped:

use ::maybe_dangling::MaybeDangling;

fn main() {
    let s: String = "I will dangle".into();
    let v = MaybeDangling::new(Box::new(&s));
    drop(s); // <- makes `&s` dangle
} // <- `v`, and thus `Box(&s)` dropped here
Category 3: T has drop glue (potentially) involving 'dangling (e.g., T = PrintOnDrop<&'dangling str>)

Like the second category, T now has drop glue. But unlike category 2., T now has drop glue either involving 'dangling or not informing the drop checker that 'dangling is unused. Let’s look at an example where 'dangling is involved in drop glue:

use ::maybe_dangling::MaybeDangling;

use ::std::fmt::Debug;

struct PrintOnDrop<T: Debug>(T);

impl<T: Debug> Drop for PrintOnDrop<T> {
    fn drop(&mut self) {
         println!("Using the potentially dangling `T` in our destructor: {:?}", self.0);
    }
}

fn main() {
    let s: String = "I will dangle".into();
    let v = MaybeDangling::new(PrintOnDrop(&s));
    drop(s); // <- makes `&s` dangle
} // <- `v`, and thus `PrintOnDrop(&s)` dropped here, causing a use-after-free ! ⚠️

The example above should never be allowed to compile as PrintOnDrop will dereference &'dangling str, which points to a str that already got dropped and invalidated, causing undefined behavior.

An example for a type where 'dangling is not involved in any drop glue but does not relax the drop checker with #[may_dangle] would be:

use ::maybe_dangling::MaybeDangling;

struct MerelyDrop<T>(T);

impl<T> Drop for MerelyDrop<T> {
    fn drop(&mut self) {
         println!("Not using the potentially dangling `T` in our destructor");
    }
}

fn main() {
    let s: String = "I will dangle".into();
    let v = MaybeDangling::new(MerelyDrop(&s));
    drop(s); // <- makes `&s` dangle
} // <- `v`, and thus `MerelyDrop(&s)` dropped here

To amend the example above and move from category 3. to category 2. and make it compile, #[may_dangle] can be applied to T in MerelyDrop’s Drop implementation:

#![feature(dropck_eyepatch)]

use ::maybe_dangling::MaybeDangling;

struct MerelyDrop<T>(T);

unsafe impl<#[may_dangle] T> Drop for MerelyDrop<T> {
    fn drop(&mut self) {
         println!("Not using the potentially dangling `T` in our destructor");
    }
}

fn main() {
    let s: String = "I will dangle".into();
    let v = MaybeDangling::new(MerelyDrop(&s));
    drop(s); // <- makes `&s` dangle
} // <- `v`, and thus `MerelyDrop(&s)` dropped here

Note that the Drop implementation is unsafe now, as we are still free to use the dangling T in the destructor. We only pinky-swear to the drop checker that we won’t.

Summary: when is a MaybeDangling<...'dangling...> allowed to go out of scope

This table summarises which of the categories shown above can be compiled, with or without the nightly-dropck_eyepatch feature enabled:

MaybeDangling<T>

where T
With nightly-dropck_eyepatchWithout nightly-dropck_eyepatch
has no drop glue
e.g.
T = &'dangling str
has drop glue known not to involve 'dangling
e.g.
T = Box<&'dangling str>
has drop glue (potentially) involving 'dangling
e.g.
T = PrintOnDrop<&'dangling str>

Implementations§

source§

impl<T> MaybeDangling<T>

source

pub const fn new(value: T) -> MaybeDangling<T>

source

pub fn into_inner(slot: MaybeDangling<T>) -> T

Extracts the value from the MaybeDangling container.

See ::core::mem::ManuallyDrop::into_inner() for more info.

Trait Implementations§

source§

impl<T: Clone> Clone for MaybeDangling<T>

source§

fn clone(&self) -> Self

Returns a copy of the value. Read more
source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<T: Debug> Debug for MaybeDangling<T>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

source§

impl<T: Default> Default for MaybeDangling<T>

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl<T> Deref for MaybeDangling<T>

§

type Target = T

The resulting type after dereferencing.
source§

fn deref(&self) -> &T

Dereferences the value.
source§

impl<T> DerefMut for MaybeDangling<T>

source§

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

Mutably dereferences the value.
source§

impl<T> Drop for MaybeDangling<T>

source§

fn drop(&mut self)

Executes the destructor for this type. Read more
source§

impl<T: Hash> Hash for MaybeDangling<T>

source§

fn hash<__H: Hasher>(&self, state: &mut __H)

1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
source§

impl<T: Ord> Ord for MaybeDangling<T>

source§

fn cmp(&self, other: &Self) -> Ordering

1.21.0 · source§

fn max(self, other: Self) -> Self
where Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · source§

fn min(self, other: Self) -> Self
where Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · source§

fn clamp(self, min: Self, max: Self) -> Self
where Self: Sized + PartialOrd,

Restrict a value to a certain interval. Read more
source§

impl<T: PartialEq> PartialEq for MaybeDangling<T>

source§

fn eq(&self, other: &Self) -> bool

1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
source§

impl<T: PartialOrd> PartialOrd for MaybeDangling<T>

source§

fn partial_cmp(&self, other: &Self) -> Option<Ordering>

1.0.0 · source§

fn lt(&self, other: &Rhs) -> bool

This method tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · source§

fn le(&self, other: &Rhs) -> bool

This method tests less than or equal to (for self and other) and is used by the <= operator. Read more
1.0.0 · source§

fn gt(&self, other: &Rhs) -> bool

This method tests greater than (for self and other) and is used by the > operator. Read more
1.0.0 · source§

fn ge(&self, other: &Rhs) -> bool

This method tests greater than or equal to (for self and other) and is used by the >= operator. Read more
source§

impl<T: Eq> Eq for MaybeDangling<T>

Auto Trait Implementations§

§

impl<T> RefUnwindSafe for MaybeDangling<T>
where T: RefUnwindSafe,

§

impl<T> Send for MaybeDangling<T>
where T: Send,

§

impl<T> Sync for MaybeDangling<T>
where T: Sync,

§

impl<T> Unpin for MaybeDangling<T>
where T: Unpin,

§

impl<T> UnwindSafe for MaybeDangling<T>
where T: 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<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<T, U> TryFrom<U> for T
where U: Into<T>,

§

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>,

§

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.