How can I allocate aligned memory in Rust?

For FFI, I'd like to allocate aligned memory with a size that's defined at runtime. It'd be great if it's a vector, but it doesn't look like it's possible. Is there a cross-platform way to allocate aligned memory?

To be clear, this is what I'm looking for, approximately:

let size = get_object_size();
let alignment = size; // can only be 1-8 bytes
let v: Vec<u8> = Vec::new();
v.allocate_aligned(size, alignment);
assert_eq!(v.as_ptr() as i64 & (alignment - 1), 0);
assert_eq!(v.len(), size);
2 Likes

By default, Vec<T> guarantees that memory is properly aligned for type T. If you want to, say, allocate an over-aligned slice of bytes, you may find the "alloc" module of std interesting.

Once you have gotten you allocations the way you want, you can use std::slice::from_raw_parts[_mut] to wrap them behind a nicer Rust slice interface, but be sure to check that function's documentation as it has some important preconditions.

One (not so) obvious bit which the slice::from_raw_parts documentation does not mention is that you must initialize your data before building a Rust slice pointing to it.

Vec::from_raw_parts is possible as well, but probably too dangerous, because any reallocation would destroy all your hard work!

2 Likes

It would also cause undefined behavior as memory must be deallocated with the same alignment it was allocated with.

2 Likes

Nice catch, thanks!

1 Like

Sounds like you're looking for std::alloc::alloc(). It's given a Layout (size + alignment) and will return a raw pointer to some allocated memory using the global allocator.

This is all unsafe of course, but if you're doing FFI then I don't see it being a problem.

There's also Alloc::alloc_array(), but that requires a handle to an existing allocator, and the allocator API isn't stable yet.

1 Like

Quick-and-dirty hack

In the case where the alignment is one of 1, 2, 4, 8, picking 8 since the beginning removes the "runtime" dependency and allows to just allocate at least size bytes in memory by creating a Vec::<u64>::with_capacity(size / 8 + 1).

The proper way:

use ::core::ptr;
use ::std::alloc; // or extern crate alloc; use ::alloc::alloc;

fn alloc (numbytes: usize, alignment: usize)
  -> Option<ptr::NonNull<()>>
{Some({
    if numbytes == 0 { return None; }
    let layout =
        alloc::Layout::from_size_align(numbytes, alignment)
            .map_err(|err| eprintln!("Layout error: {}", err))
            .ok()?
    ;
    ptr::NonNull::new(unsafe {
        // # Safety
        //
        //   - numbytes != 0
        alloc::alloc(layout)
    })?
        .cast::<()>()
})}

/// # Safety
///
///   - `ptr`, when `NonNull`, must be a value returned by `alloc(numbytes, alignment)`
#[require_unsafe_in_body] // or #[allow(unused_unsafe)]
unsafe
fn free (
    ptr: Option<ptr::NonNull:<()>>,
    numbytes: usize,
    alignment: usize,
)
{
    let ptr = if let Some(ptr) = ptr { ptr } else { return; };
    let layout =
        alloc::Layout::from_size_align(numbytes, alignment)
            .unwrap_or_else(|err| {
                // if same layout as input this should not happen,
                // so it is a very bad bug if this is reached
                eprintln!("Layout error: {}", err);
                ::std::proces::abort();
            })
    ;
    unsafe {
        // # Safety
        //
        //   - `ptr` came from alloc::alloc(layout);
        alloc::dealloc(ptr.cast::<u8>().as_ptr(), layout);
    }
}
  • Where I have used #[require_unsafe_in_body] to remove Rust mistakenly not requiring unsafe blocks in unsafe fn function bodies.
2 Likes