pub struct StackBox<'frame, T: ?Sized + 'frame> { /* private fields */ }
Expand description
Stack1-allocated Box
. Think of this as of &'frame mut T
, but
with move
semantics (no reborrowing!) which allow the “reference” to drop
its pointee.
1 Pedantic nit: actually, it is local-allocated: if the
local is created inside a generator such as an async
block or function,
crossing a yield
/ .await
point (thus captured by the generator),
and that generator / future is Box
-ed, then the local will be living on
the heap.
Given the move
semantics / lack of reborrowing, there may seem to be
little point in using this over the seemingly more flexible
&'frame mut T
, or the clearly more simple T
.
And indeed that is mostly true: the usage of this wrapper is a bit niche. Use this wrapper when:
-
You want / need the move semantics (
FnOnce
) ⇒ no&mut
for you (assumingOption::take
is too cumbersome, costly or directly unusable for your use case). -
You need the indirection:
-
if
T
is big, and you need move semantics, movingT
around may be expensive if the compiler is not able to elide the bitwise-copies (memcpy
) that happen when the value is moved. §Main usage
If you need a fat pointer to perform some type erasure, while preserving ownership /
move
semantics, and you don’t want (or actually can’t) use the heap allocation fromBox
, then this type is for you!
-
§Examples of type erasure
§1 - Array to slice coercion and IntoIter
IntoIterator
for arrays slices:
use ::stackbox::prelude::*;
stackbox!(let boxed_slice: StackBox<'_, [_]> = [
String::from("Hello, "),
String::from("World!"),
]);
for s in boxed_slice {
println!("{}", s);
stuff::<String>(s);
}
-
or with some
#[with]
sugar::use ::stackbox::prelude::*; use ::with_locals::with; #[with('local)] fn main () { let boxed_array: StackBox<'local, [String; 2]> = StackBox::new([ String::from("Hello, "), String::from("World!"), ]); let boxed_slice: StackBox<'_, [String]> = boxed_array.into_slice(); for s in boxed_slice { println!("{}", s); stuff::<String>(s); } }
While &mut [T; N] → &mut [T]
already covers most of the use cases,
imagine needing the [T]
slice type erasure (e.g., an if
branch which
yields arrays of different lengths) and also needing to have
IntoIterator
available to you. And you don’t want to “stupidly” pay a
heap allocation for something that should not deserve one:
use ::core::mem::ManuallyDrop;
use ::stackbox::prelude::*;
mk_slots!(storage1, storage2); // uninit stack allocations.
let boxed_slice_of_strings: StackBox<'_, [String]> =
if some_condition() {
StackBox::new_in(storage1, [
String::from("Hi."),
])
.into_slice() // [String; 1] → [String]
} else {
// If using the macro, the coercion happens automagically
stackbox!(storage2, [
"Hello, ".into(),
"World!".into(),
])
}
;
for s in boxed_slice_of_strings {
println!("{}", s);
stuff::<String>(s);
}
§2 - Allocation-less dyn FnOnce
(and owned dyn Any
)
See the dedicated module for more info.
use ::stackbox::prelude::*;
mk_slots!(f1, f2);
let f: StackBoxDynFnOnce_0<()> = if some_condition() {
f1.stackbox(move || {
// …
}).into_dyn()
} else {
f2.stackbox(move || {
// …
}).into_dyn()
};
// …
f.call();
Implementations§
Source§impl<'frame, ImplTrait: 'frame> StackBox<'frame, ImplTrait>
impl<'frame, ImplTrait: 'frame> StackBox<'frame, ImplTrait>
Sourcepub fn into_dyn<StackBoxDynTrait>(
self: StackBox<'frame, ImplTrait>,
) -> StackBoxDynTraitwhere
StackBoxDynTrait: DynCoerce<StackBox<'frame, ImplTrait>>,
pub fn into_dyn<StackBoxDynTrait>(
self: StackBox<'frame, ImplTrait>,
) -> StackBoxDynTraitwhere
StackBoxDynTrait: DynCoerce<StackBox<'frame, ImplTrait>>,
Coerces a StackBox<impl Trait>
into a StackBox<dyn Trait>
, provided
the Trait
is one of the supported ones.
Source§impl<'frame, Item: 'frame> StackBox<'frame, [Item]>
impl<'frame, Item: 'frame> StackBox<'frame, [Item]>
Sourcepub fn stackbox_pop_first(self: &mut StackBox<'frame, [Item]>) -> Option<Item>
pub fn stackbox_pop_first(self: &mut StackBox<'frame, [Item]>) -> Option<Item>
Sourcepub fn stackbox_pop_last(self: &mut StackBox<'frame, [Item]>) -> Option<Item>
pub fn stackbox_pop_last(self: &mut StackBox<'frame, [Item]>) -> Option<Item>
Source§impl<'frame, Item: 'frame, const N: usize> StackBox<'frame, [Item; N]>
impl<'frame, Item: 'frame, const N: usize> StackBox<'frame, [Item; N]>
Sourcepub fn into_slice(self: StackBox<'frame, [Item; N]>) -> StackBox<'frame, [Item]>
pub fn into_slice(self: StackBox<'frame, [Item; N]>) -> StackBox<'frame, [Item]>
Coerces a StackBox<[T; N]>
into a StackBox<[T]>
.
-
Note that you may not need to use
.into_slice()
if instead ofStackBox::new_in
you usestackbox!
to construct it:use ::stackbox::prelude::*; mk_slots!(slot); // boxed_slice: StackBox<'_, [String]> = StackBox::new_in(slot, [ let mut boxed_slice: StackBox<'_, [String]> = stackbox!(slot, [ "Hello, World!".into() ]); let _: String = boxed_slice.stackbox_pop_first().unwrap();
Source§impl<'frame, T: 'frame> StackBox<'frame, T>
impl<'frame, T: 'frame> StackBox<'frame, T>
Sourcepub fn new_in(slot: &'frame mut Slot<T>, value: T) -> StackBox<'frame, T>
pub fn new_in(slot: &'frame mut Slot<T>, value: T) -> StackBox<'frame, T>
§Main non-unsafe
non-macro non-callback constructor.
To be used most of the time (when T : Sized
, and when no implicit
implicit coercion is needed).
- Creation of the
Slot
s is possible either manually, by binding the return value ofmk_slot()
to some variable, byref mut
, or if multiple slots are needed, they can be batch created thanks to themk_slots!
convenience helper macro.
§Example
use ::stackbox::prelude::*;
let slot = &mut mk_slot();
let boxed = if true {
StackBox::new_in(slot, 42)
} else {
StackBox::new_in(slot, 27)
};
assert_eq!(*boxed, 42);
Sourcepub fn with_new<R, F>(value: T, ret: F) -> R
pub fn with_new<R, F>(value: T, ret: F) -> R
Alternative non-unsafe
non-macro constructor, where instead of an
explicit Slot
that defines the scope of validity of the
StackBox
(its stack frame), a callback is used: the StackBox
is
valid for the duration of the callback.
§Example
use ::stackbox::prelude::*;
StackBox::with_new(42, |stackbox: StackBox<'_, i32>| {
let any: StackBoxDynAny<'_> = stackbox.into_dyn();
assert_eq!(
any.downcast_ref::<i32>().unwrap(),
&42,
);
}) // <- `StackBox` cannot outlive this point.
§Ergonomic usage thanks to #[::with_locals::with]
Using this constructor can be made quite ergonomic by using the
#[with]
CPS sugar:
use ::stackbox::prelude::*;
use ::with_locals::with;
#[with]
fn main ()
{
let stackbox: StackBox<'ref, /* … */> = StackBox::new({
/* … */
});
// …
}
Source§impl<'frame, T: ?Sized + 'frame> StackBox<'frame, T>
impl<'frame, T: ?Sized + 'frame> StackBox<'frame, T>
Sourcepub unsafe fn assume_owns(
it: &'frame mut ManuallyDrop<T>,
) -> StackBox<'frame, T>
pub unsafe fn assume_owns( it: &'frame mut ManuallyDrop<T>, ) -> StackBox<'frame, T>
Raw unsafe
constructor, by taking ownership of a borrowing pointer.
§Safety
This type has ownership of the pointee T
. This means that despite the
borrow-looking nature of the &'frame mut
, the pointee should not be
used (⇒ not dropped!) once it has been pointed to by a StackBox
/
given to this function: the ManuallyDrop<T>
pointee will represent
deallocated memory after the 'frame
lifetime!
As a rule of thumb, it is sound to call this function when and only
when calling ManuallyDrop::drop
is.
When possible (T : Sized
), prefer to use the non-unsafe
constructors:
-
Either
StackBox::new_in
(e.g.,Sized
case),- (or the CPS / callback-based
StackBox::with_new
constructor).
- (or the CPS / callback-based
-
Or the
stackbox!
macro, for most usages.
Trait Implementations§
Source§impl<'frame, T: 'frame, U: ?Sized + 'frame> CoerciblePtr<U> for StackBox<'frame, T>
Allows conversion to a StackBox
containing an unsized type.
impl<'frame, T: 'frame, U: ?Sized + 'frame> CoerciblePtr<U> for StackBox<'frame, T>
Allows conversion to a StackBox
containing an unsized type.
§Usage
use core::fmt::Display;
use unsize::{Coercion, CoerceUnsize};
use stackbox::prelude::*;
let slot = &mut mk_slot();
let num = StackBox::<usize>::new_in(slot, 42);
let display: StackBox<dyn Display> = num.unsize(Coercion::to_display());