1. Symptoms
When the Rust compiler encounters error E0795, you will see output similar to the following:
error[E0795]: function pointer types that differ only in their alignment
--> src/main.rs:10:5
|
10 | let fp: fn() = extern_func;
| ^^^^^^^^^^^^^^^^ expected function pointer with alignment 1, found one with alignment 8
|
= note: `extern_func` points to a function with stricter alignment requirements than what the target type accepts
= note: this commonly occurs when passing function pointers across FFI boundaries with mismatched ABIs
In more severe cases involving extern blocks, the error may manifest as:
error[E0795]: function pointer type has incompatible alignment for target
--> src/lib.rs:15:1
|
15 | pub extern "C" fn callback(target: *mut u8) -> usize {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected alignment 8, found alignment 1
|
= help: ensure the function signature matches the expected ABI layout
The compiler may also suggest that the issue stems from function pointer types being passed to or returned from functions with conflicting alignment constraints. This error frequently appears when working with foreign function interfaces, callback mechanisms, or when manually managing function pointer conversions across different calling conventions.
2. Root Cause
The underlying cause of Rust error E0795 stems from fundamental incompatibilities between function pointer types at the ABI (Application Binary Interface) level. In Rust, function pointers carry not just the address of the function, but also metadata about their expected alignment, calling convention, and safety properties. When the compiler detects that a function pointer is being coerced or assigned to a type with incompatible alignment requirements, it halts compilation to prevent undefined behavior.
This alignment mismatch typically originates from several distinct scenarios. First, when defining extern "C" blocks, the function signatures must precisely match what the foreign code expects. If a Rust function pointer type is declared with one alignment requirement but the actual function or callback expects different alignment, the compiler rejects the conversion. Second, when working with function pointers that differ in their safety context (safe vs. unsafe function pointers), the compiler enforces strict separation because calling conventions may differ. Third, when passing function pointers as type parameters or generic arguments, the concrete types must satisfy the alignment constraints imposed by the function signature.
The alignment properties of function pointers in Rust are determined by the function’s signature, its ABI, and any additional attributes applied to the function. Different calling conventions such as extern "C", extern "stdcall", and extern "system" each impose their own alignment requirements. When these requirements conflict, the compiler cannot perform the implicit coercion that might otherwise bridge the gap, resulting in E0795.
Additionally, when using function pointers in structures or enums that are themselves passed across FFI boundaries, the alignment of the containing type must be compatible with the alignment requirements of the function pointer. If a struct contains a function pointer field and that struct is used in a context expecting a different alignment, the entire type becomes incompatible.
3. Step-by-Step Fix
Resolving E0795 requires identifying the source of the alignment mismatch and either correcting the type annotations or adjusting the function signatures to be compatible. Below are the primary resolution strategies.
Solution 1: Match Function Pointer Types Exactly
When the error occurs due to type mismatch in function pointer assignments, ensure the source and target types have identical signatures and alignment properties.
Before:
type CallbackFn = fn(*mut u8) -> i32;
extern "C" {
fn register_callback(cb: fn(*mut u8) -> i32);
}
fn my_callback(data: *mut u8) -> i32 {
// Implementation
0
}
unsafe {
register_callback(my_callback); // Error: alignment mismatch
}
After:
type CallbackFn = fn(*mut u8) -> i32;
extern "C" {
fn register_callback(cb: CallbackFn);
}
unsafe extern "C" fn my_callback(data: *mut u8) -> i32 {
// Implementation
0
}
unsafe {
register_callback(my_callback); // Correct: types match
}
Solution 2: Use Unsafe Blocks with Proper Type Casting
When the alignment requirements genuinely differ but you need to perform the conversion, use an unsafe block with explicit casting through a transmutation approach.
Before:
extern "C" {
fn get_weak_callback() -> fn() -> ();
}
fn strong_callback() {}
let weak_ptr = unsafe { get_weak_callback() };
let strong_ptr = weak_ptr as fn() -> (); // E0795
After:
extern "C" {
fn get_weak_callback() -> fn() -> ();
}
fn strong_callback() {}
unsafe {
let weak_ptr = get_weak_callback();
// Transmute through a compatible intermediate representation
let strong_ptr: fn() -> () = std::mem::transmute(weak_ptr);
}
Solution 3: Correct Extern Block Declarations
When defining extern functions, ensure the signatures precisely match the expected foreign interface.
Before:
// Incorrect: missing alignment attribute
extern "C" {
fn process_data(data: *const u8, len: usize) -> i32;
}
After:
// Correct: explicit alignment if needed
#[repr(C)]
struct DataHeader {
value: i32,
}
extern "C" {
#[cfg(target_arch = "x86_64")]
fn process_data(data: *const u8, len: usize) -> i32;
}
Solution 4: Align Function Pointers in Structs
When using function pointers as struct fields, ensure proper alignment directives.
Before:
struct CallbackHolder {
callback: fn() -> i32,
data: u64,
}
impl Default for CallbackHolder {
fn default() -> Self {
Self {
callback: || 0, // Error: closure has different alignment
data: 0,
}
}
}
After:
struct CallbackHolder {
callback: fn() -> i32,
#[repr(align(8))]
_padding: [u8; 0],
data: u64,
}
impl Default for CallbackHolder {
fn default() -> Self {
Self {
callback: || 0,
_padding: [],
data: 0,
}
}
}
4. Verification
After implementing a fix for E0795, verify that the compilation succeeds and the runtime behavior remains correct. The verification process involves multiple steps to ensure both type safety and functional correctness.
First, compile the affected crate with verbose output to confirm the error no longer appears:
cargo build --verbose 2>&1 | grep -E "(E0795|Compiling|Finished)"
Second, if the code involves FFI boundaries, write integration tests that exercise the function pointer code paths:
#[cfg(test)]
mod tests {
use std::ptr;
#[test]
fn test_callback_registration() {
extern "C" {
static CALLBACK_TABLE: *const fn() -> i32;
}
unsafe {
assert!(!CALLBACK_TABLE.is_null());
}
}
}
Third, on platforms that support it, run the binary with address sanitizer to detect any memory alignment issues that might not cause immediate compilation errors but could manifest at runtime:
RUSTFLAGS="-Z sanitizer=address" cargo test
Finally, verify that any existing unit tests continue to pass, as incorrect fixes may compile but introduce subtle runtime bugs:
cargo test --lib
cargo test --doc
5. Common Pitfalls
When resolving E0795, developers frequently encounter several recurring mistakes that can complicate the debugging process or introduce new issues.
A frequent error is attempting to coerce function pointers using the as cast without understanding the underlying alignment constraints. While Rust allows some function pointer conversions, mismatched alignment cannot be bridged through simple casting. Developers should use std::mem::transmute only when they are certain the alignment requirements are compatible, as improper transmutation leads to undefined behavior.
Another common pitfall involves forgetting that closures do not have the same alignment properties as named function pointers. When passing closures as callbacks to FFI functions, the closure’s capture environment adds additional data that changes the function pointer’s representation. Always convert closures to named functions when working with extern boundaries, or use mechanism like Box<dyn Fn> with proper extern type wrappers.
Platform-specific alignment differences also cause issues. Code that compiles correctly on x86_64 may fail on ARM platforms where alignment requirements differ. Always test FFI code on all target platforms, and use conditional compilation with #[cfg(target_arch = "...")] when necessary.
Forgetting to mark functions as unsafe when they contain FFI interactions is another common mistake. If a function pointer assignment involves unsafe operations, the surrounding code must be within an unsafe block or function, otherwise the compiler may report confusing errors alongside E0795.
Finally, modifying function pointer types in public API boundaries without corresponding updates to call sites causes cascading errors. When fixing E0795, audit all usage sites of the affected function pointer type to ensure consistency throughout the codebase.
6. Related Errors
Error E0795 frequently appears alongside several other Rust compiler errors that share common causes related to FFI and function pointer handling.
E0603 (private module in public API) can occur when the function pointer type is defined in a private module but exposed through a public interface, creating confusion about the exact type signature during compilation. This error often surfaces when refactoring FFI code and accidentally exposing internal implementation details.
E0733 (async closures are not yet supported) occasionally appears when developers attempt to use async closures as function pointers in FFI contexts. Since async functions have different representation than regular functions, they cannot be coerced to function pointer types, leading to compound errors involving both E0733 and alignment-related issues.
E0520 (conflicting implementations of trait) can arise when function pointer types are used as generic parameters and multiple trait implementations become ambiguous due to the pointer’s alignment properties. This typically happens in complex generic code involving function pointers and trait bounds.
E0133 (dereferencing raw pointers in safe function) is often related because both raw pointer manipulation and function pointer alignment involve unsafe Rust concepts. Code that triggers E0795 may also involve raw pointer dereferences that require explicit unsafe blocks.