How can I align a struct to a specified byte boundary?
Asked Answered
C

2

15

I need to align a struct to a 16 byte boundary in Rust. It seems possible to give hints about alignment through the repr attribute, but it doesn't support this exact use case.

A functional test of what I'm trying to achieve is a type Foo such that

assert_eq!(mem::align_of::<Foo>(), 16);

or alternatively, a struct Bar with a field baz such that

println!("{:p}", Bar::new().baz);

always prints a number divisible by 16.

Is this currently possible in Rust? Are there any work-arounds?

Counterespionage answered 6/9, 2015 at 20:47 Comment(3)
Do you mean you want to have a struct in the heap with a specific alignment? I'm not aware of alignment for stack-allocated types, but the unstable allocate does have an alignment argument.Verisimilar
Also, you mention packing as decreasing alignment; do you mean to say that you need to have a member of a struct always at a specific alignment? You may want to bolster your question with some example code, ASCII-art memory diagrams, or more prose describing why you need to do this so that all the people smarter than I can help you. ^_^Verisimilar
Thanks for the feedback, @Shepmaster! I meant aligned in general, but yes, my specific use case is for the stack. I'll update my question to be clearer.Counterespionage
S
23

huon's answer is good, but it's out of date.

As of Rust 1.25.0, you may now align a type to N bytes using the attribute #[repr(align(N))]. It is documented under the reference's Type Layout section. Note that the alignment must be a power of 2, you may not mix align and packed representations, and aligning a type may add extra padding to the type. Here's an example of how to use the feature:

#[repr(align(64))]
struct S(u8);

fn main() {
    println!("size of S: {}", std::mem::size_of::<S>());
    println!("align of S: {}", std::mem::align_of::<S>());
}
Shimkus answered 5/7, 2018 at 19:17 Comment(0)
K
11

There's no way to specify alignment directly at the moment, but it is definitely desirable and useful. It is covered by issue #33626, and its RFC issue.

A current work-around to force alignment of some struct Foo to be as large as the alignment of some type T is to include a field of type [T; 0] which has size zero and so won't otherwise affect the behaviour of the struct, e.g. struct Foo { data: A, more_data: B, _align: [T; 0] }.

On nightly, this can be combined with SIMD types to get a specific high alignment, since they have alignment equal to their size (well, the next power of two), e.g.

#[repr(simd)]
struct SixteenBytes(u64, u64);

struct Foo {
    data: A,
    more_data: B,
    _align: [SixteenBytes; 0]
}
Kyser answered 6/9, 2015 at 22:57 Comment(4)
Wouldn't Foo's alignment continue to be std::mem::align_of<A>(), but with a potentially increased std::mem::size_of::<Foo> reflecting padding inserted after B, to align _align? How is Foo's alignment affected at all by appending _align: [T; 0] to the struct'struct members? Did you mean prepend?Numismatics
The field can only be aligned statically if the start of the struct has a known alignment, because it always has a fixed offset from the start. Thus, this trick actually works no matter where the field is in the struct order.Kyser
I didn't know that--thank you. So instead of requiring (&struct + offset ) % desired_alignment == 0, the compiler ensures `&struct % desired_alignment == 0 && offset % desired_alignment == 0? That seems awfully wasteful, but I'm sure there must be a reason--can you point me to any resources on this to learn more?Numismatics
The second is the only sensible way to ensure the first that I can think of. Not having the second would mean using a variable offset, which would have to be stored somewhere, and would likely waste more space. Also note that "incredibly wasteful" is a few bytes in a few structs.Kyser

© 2022 - 2024 — McMap. All rights reserved.