Trait varlen::Initializer
source · [−]pub unsafe trait Initializer<T: VarLen> {
fn calculate_layout_cautious(&self) -> Option<T::Layout>;
unsafe fn initialize(self, dst: NonNull<T>, layout: T::Layout);
}
Expand description
A type that knows how to construct a variable-length object.
Construction of a variable-length object proceeds as follows:
- The
Initializer
calculates how much memory is required for the object, returns that information to the caller. - The caller, such as
VBox
orSeq
, reserves that much memory for the object. - The
Initializer
populates the reserved memory.
An Initializer
is responsible for steps (1) and (3) of this protocol. Step (1)
is handled by calculate_layout_cautious
,
and step (3) is handled by initialize
.
Directly defining or using an Initializer
involves writing unsafe
code.
You can typically avoid this unsafe
code by using the standard container types
such as VBox
aod Seq
and the standard
initializers that the tuple/struct/array/string types provide.
Examples
Full lifecycle of initialization and destruction:
use varlen::prelude::*;
use varlen::Layout as _;
use std::alloc::Layout;
use std::pin::Pin;
use std::ptr::NonNull;
// Make an initializer. This is a cheap pointer-only operation.
let init = Str::copy("hello world");
// Step 1: calculate memory requirement.
let layout = init.calculate_layout_cautious().unwrap();
let alloc_layout = Layout::from_size_align(layout.size(), Str::<usize>::ALIGN).unwrap();
let mut s: Pin<&mut Str> = unsafe {
// Step 2: allocate memory.
let mem = std::alloc::alloc(alloc_layout);
let mut mem = NonNull::new(mem as *mut Str).unwrap();
// Step 3: populate the memory.
init.initialize(mem, layout);
// Now use the initialized object
Pin::new_unchecked(mem.as_mut())
};
// Read its value
assert_eq!(&s[..], "hello world");
// Eventually delete it
unsafe {
let layout = s.calculate_layout();
let ptr = s.as_mut().get_unchecked_mut() as *mut _ as *mut u8;
s.vdrop(layout);
std::alloc::dealloc(ptr, alloc_layout);
}
Safety
An implementor of this trait is required to ensure:
- When
self.initialize(dst, layout)
is called withlayout=self.calculate_layout_cautious()
, theinitialize()
function doesn’t write past offsetlayout.size()
ofdst
. - When
initialize(dst, layout)
is called with layout=self.calculate_layout_cautious(), then after the
initialize()function returns the object
Tis "initialized", i.e. it is safe to use as a
&Tor as a
Pin<&mut T>`. - The layout returned by
calculate_layout_cautious()
matches the layout returned byVarLen::calculate_layout()
on the initialized object.
Required methods
fn calculate_layout_cautious(&self) -> Option<T::Layout>
fn calculate_layout_cautious(&self) -> Option<T::Layout>
Calculates the layout of the object, returning None
if any of the calculated sizes
or offsets would overflow usize
.
Examples
use varlen::prelude::*;
use varlen::Layout as _;
// Succeeds on most sizes:
let init = SizedInit(4usize, FillWithDefault);
assert_eq!(
Initializer::<Array<u32>>::calculate_layout_cautious(&init)
.unwrap().size(),
std::mem::size_of::<usize>() + 4 * std::mem::size_of::<u32>());
// Fails on `usize` overflow:
let init = SizedInit(usize::MAX, FillWithDefault);
assert!(
Initializer::<Array<u32>>::calculate_layout_cautious(&init)
.is_none());
unsafe fn initialize(self, dst: NonNull<T>, layout: T::Layout)
unsafe fn initialize(self, dst: NonNull<T>, layout: T::Layout)
Populates the destination pointer.
See the trait documentation for details on the initialization protocol.
Safety
Trait implementor requirements are documented in the trait documentation.
Additionally, the function caller must guarantee:
- You must call
initialize
with the layout returned bycalculate_layout_cautious()
on the same initializer object. dst
must be writable, with size as specified byself.calculate_layout().size()
.
Implementors
impl<'a, A: VClone<'a>, B: VClone<'a>> Initializer<Tup2<A, B>> for varlen::tuple::tup2::Cloner<'a, A, B>
impl<'a, A: VClone<'a>, B: VClone<'a>, C: VClone<'a>> Initializer<Tup3<A, B, C>> for varlen::tuple::tup3::Cloner<'a, A, B, C>
impl<'a, A: VClone<'a>, B: VClone<'a>, C: VClone<'a>, D: VClone<'a>> Initializer<Tup4<A, B, C, D>> for varlen::tuple::tup4::Cloner<'a, A, B, C, D>
impl<'a, A: VClone<'a>, B: VClone<'a>, C: VClone<'a>, D: VClone<'a>, E: VClone<'a>> Initializer<Tup5<A, B, C, D, E>> for varlen::tuple::tup5::Cloner<'a, A, B, C, D, E>
impl<'a, Len: ArrayLen> Initializer<Str<Len>> for StrCloner<'a, Len>
impl<'a, T: Clone> Initializer<FixedLen<T>> for FixedLenCloner<'a, T>
impl<'a, T: Clone, Len: ArrayLen> Initializer<Array<T, Len>> for ArrayCloner<'a, T, Len>
impl<'a, T: VCopy<'a>> Initializer<T> for VCopier<'a, T>
impl<A: VarLen, AInit: Initializer<A>, B: VarLen, BInit: Initializer<B>> Initializer<Tup2<A, B>> for varlen::tuple::tup2::Init<AInit, BInit>
impl<A: VarLen, AInit: Initializer<A>, B: VarLen, BInit: Initializer<B>, C: VarLen, CInit: Initializer<C>> Initializer<Tup3<A, B, C>> for varlen::tuple::tup3::Init<AInit, BInit, CInit>
impl<A: VarLen, AInit: Initializer<A>, B: VarLen, BInit: Initializer<B>, C: VarLen, CInit: Initializer<C>, D: VarLen, DInit: Initializer<D>> Initializer<Tup4<A, B, C, D>> for varlen::tuple::tup4::Init<AInit, BInit, CInit, DInit>
impl<A: VarLen, AInit: Initializer<A>, B: VarLen, BInit: Initializer<B>, C: VarLen, CInit: Initializer<C>, D: VarLen, DInit: Initializer<D>, E: VarLen, EInit: Initializer<E>> Initializer<Tup5<A, B, C, D, E>> for varlen::tuple::tup5::Init<AInit, BInit, CInit, DInit, EInit>
impl<T> Initializer<FixedLen<T>> for FixedLen<T>
impl<T, Len: ArrayLen, Init: ArrayInitializer<T>> Initializer<Array<T, Len>> for SizedInit<Init, Len>
impl<T: VarLen> Initializer<T> for Owned<'_, T>
impl<T: VarLen> Initializer<T> for VBox<T>
VBox<T>
is an initializer for T
.
Examples
Pushing a VBox<T>
onto a crate::seq::Seq<T>
:
use varlen::prelude::*;
let mut seq: Seq<Str> = Seq::new();
let b = VBox::new(Str::copy("hello"));
seq.push(b);