Zero-Copy Deserialization
Access serialized data without deserializing it
rkyv lays out serialized bytes so that the buffer itself is a valid,
aligned in-memory representation of the data. Deriving Archive on
Player generates an ArchivedPlayer type whose fields are archived
counterparts such as ArchivedString and ArchivedVec.
rkyv::to_bytes serializes the value into an AlignedVec.
rkyv::access validates the buffer and returns a &ArchivedPlayer that
points straight into it, so reading fields requires no parsing, copying, or
allocation. Archived integers are stored in a fixed byte order, so
to_native converts them to native integers for arithmetic. When an owned
value is needed, rkyv::deserialize converts the archive back into a
Player.
use rkyv::rancor::Error;
use rkyv::{Archive, Deserialize, Serialize};
#[derive(Archive, Serialize, Deserialize, Debug, PartialEq)]
struct Player {
name: String,
level: u32,
scores: Vec<u32>,
}
fn main() -> Result<(), Error> {
let player = Player {
name: "Ferris".to_string(),
level: 42,
scores: vec![100, 250, 75],
};
let bytes = rkyv::to_bytes::<Error>(&player)?;
// Validate the buffer and view it as an `ArchivedPlayer` without copying.
let archived = rkyv::access::<ArchivedPlayer, Error>(&bytes)?;
println!("name: {}", archived.name.as_str());
println!("level: {}", archived.level);
let total: u32 = archived.scores.iter().map(|s| s.to_native()).sum();
println!("total: {total}");
// Convert back into an owned `Player` only when needed.
let owned = rkyv::deserialize::<Player, Error>(archived)?;
assert_eq!(owned, player);
Ok(())
}
Read an archive from a file
Writes a Vec<Reading> to readings.rkyv in the current working directory,
then reads it back and iterates over the archived readings in place.
rkyv::access requires the buffer to be suitably aligned for the archived
types. fs::read returns a Vec<u8> with no alignment guarantee, so the
bytes are copied into an AlignedVec before access. The archived type of
Vec<Reading> is named with rkyv::Archived.
use rkyv::util::AlignedVec;
use rkyv::{Archive, Deserialize, Serialize};
use std::error::Error;
use std::fs;
#[derive(Archive, Serialize, Deserialize, Debug)]
struct Reading {
sensor: String,
celsius: f32,
}
fn main() -> Result<(), Box<dyn Error>> {
let readings = vec![
Reading {
sensor: "kitchen".to_string(),
celsius: 21.5,
},
Reading {
sensor: "garage".to_string(),
celsius: 8.25,
},
];
let bytes = rkyv::to_bytes::<rkyv::rancor::Error>(&readings)?;
fs::write("readings.rkyv", &bytes)?;
// `fs::read` gives no alignment guarantee, so copy into an `AlignedVec`.
let mut buffer = AlignedVec::<16>::new();
buffer.extend_from_slice(&fs::read("readings.rkyv")?);
let archived = rkyv::access::<rkyv::Archived<Vec<Reading>>, rkyv::rancor::Error>(&buffer)?;
for reading in archived.iter() {
println!("{}: {:.2} °C", reading.sensor, reading.celsius.to_native());
}
Ok(())
}