0 Tage Serie
rust / expert
Snippet

Manuelle Allokation benutzerdefinierter dynamisch skalierter Typen auf dem Heap

Dynamisch skalierte Typen (DSTs) wie Slices können nicht einfach auf dem Stack erstellt werden. Über die Standard-Allocator-API können wir benutzerdefinierte DSTs manuell erstellen, indem wir Layouts zusammenführen, Speicher allozieren und einen Fat-Pointer mittels slice_from_raw_parts_mut zusammensetzen.

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
use std::alloc::{alloc, Layout};
use std::ptr;
#[repr(C)]
struct CustomBuffer {
id: u64,
data: [u8],
}
impl CustomBuffer {
fn new(id: u64, slice: &[u8]) -> Box<Self> {
let header_layout = Layout::new::<u64>();
let array_layout = Layout::array::<u8>(slice.len()).unwrap();
let (layout, _) = header_layout.extend(array_layout).unwrap();
let layout = layout.pad_to_align();
unsafe {
let raw_ptr = alloc(layout);
if raw_ptr.is_null() {
std::alloc::handle_alloc_error(layout);
}
let dst_ptr = ptr::slice_from_raw_parts_mut(raw_ptr, slice.len()) as *mut CustomBuffer;
ptr::write(ptr::addr_of_mut!((*dst_ptr).id), id);
ptr::copy_nonoverlapping(slice.as_ptr(), ptr::addr_of_mut!((*dst_ptr).data) as *mut u8, slice.len());
Box::from_raw(dst_ptr)
}
}
}
Erklärung
1
let (layout, _) = header_layout.extend(array_layout).unwrap();
Kombiniert das statische Header-Layout und das dynamische Slice-Layout unter Berücksichtigung von Overflows.
2
let dst_ptr = ptr::slice_from_raw_parts_mut(raw_ptr, slice.len()) as *mut CustomBuffer;
Generiert einen Fat-Pointer, der sowohl die Speicheradresse als auch die Länge des Slice-Endes enthält.
3
Box::from_raw(dst_ptr)
Konvertiert den rohen Fat-Pointer zurück in eine verwaltete Box, was eine korrekte Deallokation garantiert.