pub unsafe trait Pod:
Copy
+ Send
+ 'static { }Expand description
Marker trait for types safe to use with seqlock-stamped ring buffers.
A type is Pod (“Plain Old Data”) if every possible bit pattern
of size_of::<T>() bytes represents a valid value of T. This is
stricter than Copy — it excludes types where certain bit patterns
are undefined behavior, such as bool (only 0/1 valid), char
(must be a valid Unicode scalar), NonZero* (must be nonzero), and
references (must point to valid memory).
§Why this matters
The seqlock read protocol performs an optimistic non-atomic read that
may observe a partially-written (“torn”) value. If the torn bit pattern
violates a type’s validity invariant, this is undefined behavior even
though the value is detected and discarded by the stamp check. Pod
guarantees that no bit pattern is invalid, making torn reads harmless.
§Safety
Implementors must ensure:
TisCopy(no destructor, no move semantics).TisSend(safe to transfer across threads).- Every possible bit pattern of
size_of::<T>()bytes is a validT. Thas no padding bytes. Padding is uninitialized memory, and theatomic-slotsfeature reads the payload as atomic words — reading an uninitialized byte as part of an integer is undefined behaviour even though every initialized bit pattern is valid. Add explicit padding fields (_pad: [u8; 3]) so the whole value is initialized rather than letting the compiler insert implicit padding.
This is why multi-field tuples are not Pod: (u8, u64) has
repr(Rust) layout with 7 implicit padding bytes. Use a #[repr(C)]
struct with explicit padding fields for multi-field payloads.
§What types are NOT Pod?
| Type | Why | What to use instead |
|---|---|---|
bool | Only 0 and 1 are valid | u8 (0 = false, 1 = true) |
char | Must be valid Unicode scalar | u32 |
NonZero<u32> | Zero is invalid | u32 |
Option<T> | Discriminant has invalid patterns | u8 sentinel (e.g., 255 = None) |
enum (Rust) | Only declared variants are valid | u8 or u32 with constants |
&T, &str | Pointer must be valid | Not supported — use value types |
String, Vec | Heap-allocated, has Drop | Fixed [u8; N] buffer |
§Converting real-world types
A common pattern: your domain model uses enums and Option, but the
Photon Ring message struct uses plain integers:
// Domain type (NOT Pod — has Option and enum)
// enum Side { Buy, Sell }
// struct Order { price: f64, qty: u32, side: Side, tag: Option<u32> }
// Photon Ring message (Pod — all fields are plain numerics)
#[repr(C)]
#[derive(Clone, Copy)]
struct OrderMsg {
price: f64,
qty: u32,
side: u8, // 0 = Buy, 1 = Sell
tag: u32, // 0 = None, nonzero = Some(value)
_pad: [u8; 3], // explicit padding for alignment
}
unsafe impl photon_ring::Pod for OrderMsg {}
// Convert at the boundary:
// let msg = OrderMsg { price: 100.0, qty: 10, side: 0, tag: 0, _pad: [0;3] };
// publisher.publish(msg);§Pre-implemented types
Pod is implemented for all primitive numeric types, arrays of Pod
types, and the zero- and one-element tuples. Larger tuples are excluded
because their layout may include padding.
For user-defined structs, use unsafe impl:
#[repr(C)]
#[derive(Clone, Copy)]
struct Quote {
price: f64,
volume: u32,
_pad: u32,
}
// SAFETY: Quote is #[repr(C)], all fields are plain numerics,
// and every bit pattern is a valid Quote.
unsafe impl photon_ring::Pod for Quote {}Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".