#[derive(UnsafeByteable)]Expand description
Derives the Byteable trait for a struct using transmute.
This procedural macro automatically implements the Byteable trait for structs by using
std::mem::transmute to convert between the struct and a byte array. This provides
zero-overhead serialization but requires careful attention to memory layout and safety.
§Safety
This macro generates unsafe code using std::mem::transmute. You must ensure:
-
The struct has an explicit memory layout: Use
#[repr(C)],#[repr(C, packed)], or#[repr(transparent)]to ensure a well-defined layout. -
All byte patterns are valid: Every possible combination of bytes must represent a valid value for your struct. This generally means:
- Primitive numeric types are fine (
u8,i32,f64, etc.) - Endianness wrappers are fine (
BigEndian<T>,LittleEndian<T>) - Arrays of the above are fine
- Types with invalid bit patterns are NOT safe (
bool,char, enums with discriminants, references,NonZero*types, etc.)
- Primitive numeric types are fine (
-
No padding with uninitialized memory: When using
#[repr(C)]withoutpacked, padding bytes might contain uninitialized memory. Use#[repr(C, packed)]to avoid padding, or ensure all fields are properly aligned. -
No complex types: Do not use this with:
- Types containing pointers or references (
&T,Box<T>,Vec<T>,String, etc.) - Types with invariants (
NonZero*,bool,char, enums with fields, etc.) - Types implementing
Drop
- Types containing pointers or references (
§Requirements
The struct must:
- Have a known size at compile time (no
dyntraits or unsized fields) - Not contain any generic type parameters (or they must implement
Byteable)
§Examples
§Basic usage
use byteable::{Byteable, UnsafeByteable};
#[derive(UnsafeByteable, Debug, PartialEq)]
#[repr(C, packed)]
struct Color {
r: u8,
g: u8,
b: u8,
a: u8,
}
let color = Color { r: 255, g: 128, b: 64, a: 255 };
let bytes = color.as_byte_array();
assert_eq!(bytes, [255, 128, 64, 255]);
let restored = Color::from_byte_array(bytes);
assert_eq!(restored, color);§With endianness markers
use byteable::{Byteable, BigEndian, LittleEndian, UnsafeByteable};
#[derive(UnsafeByteable, Debug)]
#[repr(C, packed)]
struct NetworkPacket {
magic: BigEndian<u32>, // Network byte order
version: u8,
flags: u8,
payload_len: LittleEndian<u16>, // Different endianness for payload
data: [u8; 16],
}
let packet = NetworkPacket {
magic: 0x12345678.into(),
version: 1,
flags: 0,
payload_len: 100.into(),
data: [0; 16],
};
let bytes = packet.as_byte_array();
// magic is big-endian: [0x12, 0x34, 0x56, 0x78]
// payload_len is little-endian: [100, 0]§With nested structs
use byteable::{Byteable, UnsafeByteable};
#[derive(UnsafeByteable, Debug, Clone, Copy)]
#[repr(C, packed)]
struct Point {
x: i32,
y: i32,
}
#[derive(UnsafeByteable, Debug)]
#[repr(C, packed)]
struct Line {
start: Point,
end: Point,
}
let line = Line {
start: Point { x: 0, y: 0 },
end: Point { x: 10, y: 20 },
};
let bytes = line.as_byte_array();
assert_eq!(bytes.len(), 16); // 4 i32s × 4 bytes each§With generics (requires bounds)
use byteable::{Byteable, UnsafeByteable};
#[derive(UnsafeByteable, Debug)]
#[repr(C, packed)]
struct Pair<T: Byteable> {
first: T,
second: T,
}
let pair = Pair {
first: 100u32,
second: 200u32,
};
let bytes = pair.as_byte_array();
assert_eq!(bytes.len(), 8);§Common Mistakes
§❌ Missing repr attribute
use byteable::UnsafeByteable;
#[derive(UnsafeByteable)] // ❌ No #[repr(...)] - undefined layout!
struct Bad {
x: u32,
y: u16,
}§❌ Using invalid types
use byteable::UnsafeByteable;
#[derive(UnsafeByteable)]
#[repr(C, packed)]
struct Bad {
valid: bool, // ❌ bool has invalid bit patterns (only 0 and 1 are valid)
}§❌ Using types with pointers
use byteable::UnsafeByteable;
#[derive(UnsafeByteable)]
#[repr(C)]
struct Bad {
data: Vec<u8>, // ❌ Contains a pointer - not safe to transmute!
}§See Also
Byteable- The trait being implementedimpl_byteable_via!- For complex typesunsafe_byteable_transmute!- Manual implementation macro