What I learned from building my own Box<>
Tim McNamara is an author of “Rust In Action” and also one of my favorite YouTuber. This video by him is one of those that caught my attention and made me imitate his code. Since I am not familiar with low-level coding I took hours to get my head around (I didn’t know anything about “alignment”). Also this was my first time to write unsafe Rust (honestly, I still don’t have any idea where to use it). Anyway I named it Trunk for my implementation, and here are some things I learned from making it:
SAMPLE CODE.expect() as a comment
Instead of .unwrap(), you can use .expect() method to tell your audience (or code reviewer) why you unwrap it without error handling.
Consider the code below - Before performing ptr::NonNull:new()
, we have already checked the error code when allocating memory (that is, match
expression in line 9). Therefore it’s safe to unwrap it right after creating the pointer. And we can put the message with .expect() method.
fn new(value: T) -> Self {
let mut memptr: *mut T = ptr::null_mut();
// Memory allocation
unsafe {
let memptr = (&mut memptr as *mut *mut T).cast();
let align = max(align_of::<T>(), size_of::<usize>());
let size = size_of::<T>();
let error_code = libc::posix_memalign(memptr, align, size);
match error_code {
libc::EINVAL => panic!("alignment incorrect"),
libc::ENOMEM => panic!("no memory"),
_ => (),
}
}
let ptr = {
ptr::NonNull::new(memptr).expect("Guaranteed non-null if posix_memalign returns 0")
};
// ...
}
Memory alignment
This is a really interesting point as we rarely thing about it when developing web frontend and backend. Memory alignment is a process of allocating aligned memory space.
For convenience let’s consider a 32-bit CPU. CPU doesn’t read (or write) data from memory one byte at a time. Instead CPU accesses memory in 2, 4, 8, 16, or 32 byte chunks at a time. So data is accessed more efficiently when it’s stored at memory address that are multiples of certain values, such as 2, 4, 8, 16, 32 bytes. This alignment allows our CPU to access data in a single operation, without having retrieve it in multiple steps or perform additional bit manipulations.
In other words, if the data is misaligned, CPU has to perform extra work to access the data - read multiple chunks of data, shift out unwanted bytes, then combine them together.
To make it short, libc::posix_memalign
allocates specified bytes and places the address of the allocated aligned memory in *memptr.
Useful Links:
Thanks for reading ✌️