Skip to main content

fn_addr_eq

Function fn_addr_eq 

1.85.0 · Source
pub fn fn_addr_eq<T, U>(f: T, g: U) -> bool
where T: FnPtr, U: FnPtr,
Expand description

Compares the addresses of the two function pointers for equality.

This is the same as f == g, but using this function makes clear that the potentially surprising semantics of function pointer comparison are involved.

There are basically no guarantees about how functions are compiled as they have no intrinsic “identity”; in particular, this comparison:

  • May return true unexpectedly, even in cases where functions are not logically equivalent, as long as they compile to the same machine code.

    For example, the following program is likely (but not guaranteed) to print (true, true) when compiled with optimization:

    use std::hint::assert_unchecked;
    
    let f: fn(i32) -> i32 = |x| x;
    let g: fn(u32) -> u32 = |x| x + 0;  // different type, different body
    let h: unsafe fn(i32) -> i32 = |x| unsafe { assert_unchecked(x != 0); x };  // different unsafe preconditions
    // These three functions are allowed but not guaranteed to compare equal.
    dbg!(std::ptr::fn_addr_eq(f, g), std::ptr::fn_addr_eq(f, h));

    In other words, functions that compare equal can have different behavior! Even if fn_addr_eq(f, h) is true, it is still undefined behavior to call h(0), while f(0) is safe to call.

    The explanation for this is that function pointers have provenance. If functions have the same assembly and get merged, their provenance decides which variant of the function will be called. f and h therefore end up with the same address but different provenance, and fn_addr_eq only compares the address.

  • May return false in any case.

    This is particularly likely with generic functions but may happen with any function. (From an implementation perspective, this is possible because functions may sometimes be processed more than once by the compiler, resulting in duplicate machine code.)

These limitations imply that comparing function pointers (via this function or via ==) is only useful in extremely niche circumstances. For instance, if a library maintains a set of callbacks, and doesn’t guarantee which of them are actually called (i.e., callbacks can be spuriously skipped), then it can be legitimate to deduplicate callbacks based on fn_addr_eq. This can never introduce undefined behavior because it only skips executing some of the registered callbacks.