Trait ArchiveUnsized
pub trait ArchiveUnsized: Pointee {
type Archived: ArchivePointee + Portable + ?Sized;
// Required method
fn archived_metadata(
&self,
) -> <Self::Archived as ArchivePointee>::ArchivedMetadata;
}
dep_rkyv
only.Expand description
A counterpart of Archive
that’s suitable for unsized types.
Unlike Archive
, types that implement ArchiveUnsized
must be serialized
separately from their owning object. For example, whereas an i32
might be
laid out as part of a larger struct, a Box<i32>
would serialize the i32
somewhere in the archive and the Box
would point to it as part of the
larger struct. Because of this, the equivalent
Resolver
type for ArchiveUnsized
is always a
usize
representing the position of the serialized value.
ArchiveUnsized
is automatically implemented for all types that implement
Archive
. Nothing special needs to be done to use them with types like
Box
, Rc
, and Arc
. It is also already implemented for slices and string
slices, and the rkyv_dyn
crate can be used to archive trait objects. Other
unsized types must manually implement ArchiveUnsized
.
§Examples
This example shows how to manually implement ArchiveUnsized
for an unsized
type. Special care must be taken to ensure that the types are laid out
correctly.
use core::ops::{Deref, DerefMut};
use ptr_meta::Pointee;
use rkyv::{
access_unchecked,
primitive::ArchivedUsize,
rancor::{Error, Fallible},
ser::{Positional, Writer, WriterExt as _},
to_bytes,
traits::ArchivePointee,
Archive, ArchiveUnsized, Archived, ArchivedMetadata, Portable, RelPtr,
Serialize, SerializeUnsized,
};
// We're going to be dealing mostly with blocks that have a trailing slice
#[derive(Portable)]
#[repr(C)]
pub struct Block<H, T: ?Sized> {
head: H,
tail: T,
}
unsafe impl<H, T> Pointee for Block<H, [T]> {
type Metadata = <[T] as Pointee>::Metadata;
}
// ArchivePointee is automatically derived for sized types because pointers
// to sized types don't need to store any extra information. Because we're
// making an unsized block, we need to define what metadata gets stored with
// our data pointer.
impl<H, T> ArchivePointee for Block<H, [T]> {
// This is the extra data that needs to get stored for blocks with
// trailing slices
type ArchivedMetadata = <[T] as ArchivePointee>::ArchivedMetadata;
// We need to be able to turn our archived metadata into regular
// metadata for our type
fn pointer_metadata(
metadata: &Self::ArchivedMetadata,
) -> <Self as Pointee>::Metadata {
metadata.to_native() as usize
}
}
// We're implementing ArchiveUnsized for just Block<H, [T]>. We can still
// implement Archive for blocks with sized tails and they won't conflict.
impl<H: Archive, T: Archive> ArchiveUnsized for Block<H, [T]> {
// We'll reuse our block type as our archived type.
type Archived = Block<Archived<H>, [Archived<T>]>;
// Here's where we make the metadata for our archived type.
fn archived_metadata(&self) -> ArchivedMetadata<Self> {
// Because the metadata for our `ArchivedBlock` is the metadata of
// the trailing slice, we just need to return that archived
// metadata.
self.tail.archived_metadata()
}
}
// The bounds we use on our serializer type indicate that we need basic
// serializer capabilities, and then whatever capabilities our head and tail
// types need to serialize themselves.
impl<H, T, S> SerializeUnsized<S> for Block<H, [T]>
where
H: Serialize<S>,
T: Serialize<S>,
S: Fallible + Writer + ?Sized,
{
// This is where we construct our unsized type in the serializer
fn serialize_unsized(
&self,
serializer: &mut S,
) -> Result<usize, S::Error> {
// First, we serialize the head and all the tails. This will make
// sure that when we finally build our block, we don't accidentally
// mess up the structure with serialized dependencies.
let head_resolver = self.head.serialize(serializer)?;
let mut resolvers = Vec::new();
for tail in self.tail.iter() {
resolvers.push(tail.serialize(serializer)?);
}
// Now we align our serializer for our archived type and resolve it.
// We can't align for unsized types so we treat the trailing slice
// like an array of 0 length for now.
let result = serializer
.align_for::<Block<Archived<H>, [Archived<T>; 0]>>()?;
unsafe {
serializer.resolve_aligned(&self.head, head_resolver)?;
}
serializer.align_for::<Archived<T>>()?;
for (item, resolver) in self.tail.iter().zip(resolvers.drain(..)) {
unsafe {
serializer.resolve_aligned(item, resolver)?;
}
}
Ok(result)
}
}
let value = Box::new(Block {
head: "Numbers 1-4".to_string(),
tail: [1, 2, 3, 4],
});
// We have a Box<Block<String, [i32; 4]>> but we want to it to be a
// Box<Block<String, [i32]>>, so we need manually "unsize" the pointer.
let ptr = Box::into_raw(value);
let unsized_ptr = ptr_meta::from_raw_parts_mut::<Block<String, [i32]>>(
ptr.cast::<()>(),
4,
);
let unsized_value = unsafe { Box::from_raw(unsized_ptr) };
let bytes = to_bytes::<Error>(&unsized_value).unwrap();
let archived = unsafe {
access_unchecked::<Archived<Box<Block<String, [i32]>>>>(&bytes)
};
assert_eq!(archived.head, "Numbers 1-4");
assert_eq!(archived.tail.len(), 4);
assert_eq!(archived.tail, [1, 2, 3, 4]);
Required Associated Types§
type Archived: ArchivePointee + Portable + ?Sized
type Archived: ArchivePointee + Portable + ?Sized
The archived counterpart of this type. Unlike Archive
, it may be
unsized.
This type must implement ArchivePointee
, a trait that helps make
valid pointers using archived pointer metadata.
Required Methods§
fn archived_metadata(
&self,
) -> <Self::Archived as ArchivePointee>::ArchivedMetadata
fn archived_metadata( &self, ) -> <Self::Archived as ArchivePointee>::ArchivedMetadata
Creates the archived version of the metadata for this value.