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:
T = &'dangling strT = PrintOnDrop<&'dangling str>,T = Box<&'dangling str>,T = (u8, Box<PrintOnDrop<&'dangling str>>),T = &mut PrintOnDrop<&'dangling str>,
The key idea is that there are three categories of types here:
-
The types with no drop glue at all, i.e., types for which
mem::needs_drop::<T>()returnsfalse: types 1. and 5.Such types should be allowed to go out of scope at a point where their lifetime may be
'dangling. -
The types with drop glue known not to involve a dereference of the
&'danglingreference: 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. -
The types with drop glue (potentially) involving a dereference of the
&'danglingreference: 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,
-
merely drops its inner
T(likeBox<T>does) and -
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
TimplementsDrop(without#[may_dangle]); - or whenever
Ttransitively 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 hereTo 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 hereNote 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_eyepatch | Without nightly-dropck_eyepatch |
|---|---|---|
| has no drop glue e.g. T = &'dangling str | ✅ | ❌ |
has drop glue known not to involve 'danglinge.g. T = Box<&'dangling str> | ✅ | ❌ |
has drop glue (potentially) involving 'danglinge.g. T = PrintOnDrop<&'dangling str> | ❌ | ❌ |
Implementations§
Source§impl<T> MaybeDangling<T>
impl<T> MaybeDangling<T>
pub const fn new(value: T) -> MaybeDangling<T>
Sourcepub fn into_inner(slot: MaybeDangling<T>) -> T
pub fn into_inner(slot: MaybeDangling<T>) -> T
Extracts the value from the MaybeDangling container.
See ::core::mem::ManuallyDrop::into_inner() for more info.
Sourcepub unsafe fn drop_in_place(this: &mut MaybeDangling<T>)
pub unsafe fn drop_in_place(this: &mut MaybeDangling<T>)
Akin to ManuallyDrop::drop(): it drops the inner value in-place. Raw & unsafe
version of drop_in_place!.
Pin code can rely on this guarantee: an example.
§Safety
This API is unsafe and wildly dangerous. It is very strongly advisable to use
drop_in_place! instead.
Indeed, since MaybeDangling does have embedded drop glue, the moment this function
returns the only thing that ought to be done is immediately ::core::mem::forget()ting it
(or wrapping it in a ManuallyDrop), lest it be dropped implicitly (e.g., because of
some panic), resulting un double-drop unsoundness 😱.
As a matter of fact, this very function needs to feature an abort-on-panic guard to handle this problem internally.
Trait Implementations§
Source§impl<T: Clone> Clone for MaybeDangling<T>
impl<T: Clone> Clone for MaybeDangling<T>
Source§impl<T: Debug> Debug for MaybeDangling<T>
impl<T: Debug> Debug for MaybeDangling<T>
Source§impl<T: Default> Default for MaybeDangling<T>
impl<T: Default> Default for MaybeDangling<T>
Source§impl<T> Deref for MaybeDangling<T>
impl<T> Deref for MaybeDangling<T>
Source§impl<T> DerefMut for MaybeDangling<T>
impl<T> DerefMut for MaybeDangling<T>
Source§impl<T> Drop for MaybeDangling<T>
impl<T> Drop for MaybeDangling<T>
Source§impl<T: Hash> Hash for MaybeDangling<T>
impl<T: Hash> Hash for MaybeDangling<T>
Source§impl<T: Ord> Ord for MaybeDangling<T>
impl<T: Ord> Ord for MaybeDangling<T>
Source§fn cmp(&self, other: &Self) -> Ordering
fn cmp(&self, other: &Self) -> Ordering
See ::core::mem::ManuallyDrop::cmp() for more info.