0 day streak
rust / expert
Snippet

Designing Custom Dynamically Sized Types

Dynamically Sized Types (DSTs) like custom slices or header-prefixed arrays require manual memory allocation and fat pointer construction in Rust. We construct the layout dynamically, allocate raw memory, write components using raw pointer helper macros like `addr_of_mut!` to avoid creating invalid intermediate references, and convert it back to a `Box` via raw pointers.

snippet.rs
rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
use std::alloc::{alloc, handle_alloc_error, Layout};
use std::ptr::addr_of_mut;
#[repr(C)]
struct CustomDst {
header: u32,
data: [u8],
}
impl CustomDst {
unsafe fn new(header: u32, elements: &[u8]) -> Box<Self> {
let count = elements.len();
let layout = Layout::new::<u32>()
.extend(Layout::array::<u8>(count).unwrap())
.unwrap()
.0
.pad_to_align();
let ptr = alloc(layout);
if ptr.is_null() {
handle_alloc_error(layout);
}
let fat_ptr = std::ptr::slice_from_raw_parts_mut(ptr, count) as *mut CustomDst;
addr_of_mut!((*fat_ptr).header).write(header);
let dst_data_ptr = addr_of_mut!((*fat_ptr).data) as *mut u8;
std::ptr::copy_nonoverlapping(elements.as_ptr(), dst_data_ptr, count);
Box::from_raw(fat_ptr)
}
}
Breakdown
1
struct CustomDst {
Defines a custom Dynamically Sized Type where the final field is an unsized slice.
2
let layout = Layout::new::<u32>().extend(...)
Calculates the dynamic size and alignment for the header struct followed by the byte elements.
3
let fat_ptr = std::ptr::slice_from_raw_parts_mut(ptr, count) as *mut CustomDst;
Synthesizes a fat pointer to the custom DST by casting a slice fat pointer.
4
addr_of_mut!((*fat_ptr).header).write(header);
Writes the header field using a raw write operation to prevent constructing uninitialized references.