Struct maybe_dangling::MaybeDangling
source · 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 str
T = 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
&'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
. -
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,
-
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
T
implementsDrop
(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_eyepatch | Without 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>
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.
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§impl<T: PartialEq> PartialEq for MaybeDangling<T>
impl<T: PartialEq> PartialEq for MaybeDangling<T>
source§impl<T: PartialOrd> PartialOrd for MaybeDangling<T>
impl<T: PartialOrd> PartialOrd for MaybeDangling<T>
source§fn partial_cmp(&self, other: &Self) -> Option<Ordering>
fn partial_cmp(&self, other: &Self) -> Option<Ordering>
See ::core::mem::ManuallyDrop::partial_cmp()
for more info.
1.0.0 · source§fn le(&self, other: &Rhs) -> bool
fn le(&self, other: &Rhs) -> bool
self
and other
) and is used by the <=
operator. Read more