Search code examples
rustreferencealignmentundefined-behaviorpacked

How to handle "reference to packed field is unaligned" error?


I am revisiting old code and am now encountering an error (synthetic recreation):

error[E0793]: reference to packed field is unaligned
 --> src/main.rs:9:22
  |
9 |     println!("{:?}", my_struct.field);
  |                      ^^^^^^^^^^^^^^^
  |
  = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
  = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
  = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

How can I fix this?

NOTE: This is an attempt to create a canonical Q&A for users experiencing the titular error.


Solution

  • All references in Rust must be aligned but fields in a #[repr(packed)] may not satisfy their own alignment. Previous versions of the compiler erroneously allowed references to fields with improper alignment, but that is now an error. E0793 documentation.

    In your own code

    If you must use packed fields that aren't fully aligned, they can still be accessed and assigned to normally (like my_struct.field). However you can't use them in a context where a reference is created. And easy way to avoid that situation is to copy/move the field to another variable first:

    let field = my_struct.field;
    println!("{:?}", field);
    my_struct.field = ...;
    
    // a block expression can also be used to create a temporary value instead of a place expression
    println!("{:?}", { my_struct.field });
    

    If you have to use fields at their unaligned location, then you must access them via pointer obtained using addr_of!/addr_of_mut! and work with them via read_unaligned and write_unaligned. There are examples in the linked documentation specifically for packed structs.

    let field_ptr = std::ptr::addr_of!(my_struct.field);
    let field = unsafe { field_ptr.read_unaligned() };
    println!("{:?}", field);
    

    The other option is of course to make the field aligned by removing #[repr(packed)] altogether.

    In a dependency

    If you are encountering this error whilst compiling a dependency, then you can't really fix it yourself. In the ideal case the dependency's maintainers have already fixed the issue in a new version. So first try cargo update <DEPENDENCY>. If that doesn't fix it and its a direct dependency that has a newer semver-incompatible version, try updating it manually in your Cargo.toml (or cargo upgrade -p <DEPENDENCY> if you have that installed). If the error is in a nested dependency, try doing the same with the direct intermediate dependency. Use cargo tree -i <DEPENDENCY> to see which of your dependencies is dependent on the breaking one.

    Known cases:

    • ntapi 0.3: update to 0.4
    • tendril 0.4.2: update to 0.4.3
    • using macros on packed structs (serde-derive, pin-project, etc.) may or may not be supported

    If there is no current fix, then you could fork the dependency and [patch] it into your own package.

    Or you can downgrade your version of Rust to circumvent the error. Here are the version differences:

    • 1.53.0 made this a warning
    • 1.62.0 made this error by default (but can be allowed by #[allow(unaligned_references)])
    • 1.69.0 made this always error

    So using 1.52.1 wouldn't even warn, 1.61.0 would emit a warning but allow it, and 1.68.2 would allow it if configured to. Though keep in mind this was always undefined behavior even on these older versions; the offending code may or may not trigger errors depending on your target system.