Primitive Type fn
1.0.0 ·Expand description
Function pointers, like fn(usize) -> bool
.
See also the traits Fn
, FnMut
, and FnOnce
.
Function pointers are pointers that point to code, not data. They can be called
just like functions. Like references, function pointers are, among other things, assumed to
not be null, so if you want to pass a function pointer over FFI and be able to accommodate null
pointers, make your type Option<fn()>
with your required signature.
§Safety
Plain function pointers are obtained by casting either plain functions, or closures that don’t capture an environment:
fn add_one(x: usize) -> usize {
x + 1
}
let ptr: fn(usize) -> usize = add_one;
assert_eq!(ptr(5), 6);
let clos: fn(usize) -> usize = |x| x + 5;
assert_eq!(clos(5), 10);
In addition to varying based on their signature, function pointers come in two flavors: safe
and unsafe. Plain fn()
function pointers can only point to safe functions,
while unsafe fn()
function pointers can point to safe or unsafe functions.
fn add_one(x: usize) -> usize {
x + 1
}
unsafe fn add_one_unsafely(x: usize) -> usize {
x + 1
}
let safe_ptr: fn(usize) -> usize = add_one;
//ERROR: mismatched types: expected normal fn, found unsafe fn
//let bad_ptr: fn(usize) -> usize = add_one_unsafely;
let unsafe_ptr: unsafe fn(usize) -> usize = add_one_unsafely;
let really_safe_ptr: unsafe fn(usize) -> usize = add_one;
§ABI
On top of that, function pointers can vary based on what ABI they use. This
is achieved by adding the extern
keyword before the type, followed by the
ABI in question. The default ABI is “Rust”, i.e., fn()
is the exact same
type as extern "Rust" fn()
. A pointer to a function with C ABI would have
type extern "C" fn()
.
extern "ABI" { ... }
blocks declare functions with ABI “ABI”. The default
here is “C”, i.e., functions declared in an extern {...}
block have “C”
ABI.
For more information and a list of supported ABIs, see the nomicon’s section on foreign calling conventions.
§Variadic functions
Extern function declarations with the “C” or “cdecl” ABIs can also be variadic, allowing them
to be called with a variable number of arguments. Normal Rust functions, even those with an
extern "ABI"
, cannot be variadic. For more information, see the nomicon’s section on
variadic functions.
§Creating function pointers
When bar
is the name of a function, then the expression bar
is not a
function pointer. Rather, it denotes a value of an unnameable type that
uniquely identifies the function bar
. The value is zero-sized because the
type already identifies the function. This has the advantage that “calling”
the value (it implements the Fn*
traits) does not require dynamic
dispatch.
This zero-sized type coerces to a regular function pointer. For example:
use std::mem;
fn bar(x: i32) {}
let not_bar_ptr = bar; // `not_bar_ptr` is zero-sized, uniquely identifying `bar`
assert_eq!(mem::size_of_val(¬_bar_ptr), 0);
let bar_ptr: fn(i32) = not_bar_ptr; // force coercion to function pointer
assert_eq!(mem::size_of_val(&bar_ptr), mem::size_of::<usize>());
let footgun = &bar; // this is a shared reference to the zero-sized type identifying `bar`
The last line shows that &bar
is not a function pointer either. Rather, it
is a reference to the function-specific ZST. &bar
is basically never what you
want when bar
is a function.
§Casting to and from integers
You can cast function pointers directly to integers:
However, a direct cast back is not possible. You need to use transmute
:
let fnptr = fnptr_addr as *const ();
let fnptr: fn(i32) -> i32 = unsafe { std::mem::transmute(fnptr) };
assert_eq!(fnptr(40), 42);
Crucially, we as
-cast to a raw pointer before transmute
ing to a function pointer.
This avoids an integer-to-pointer transmute
, which can be problematic.
Transmuting between raw pointers and function pointers (i.e., two pointer types) is fine.
Note that all of this is not portable to platforms where function pointers and data pointers have different sizes.
§ABI compatibility
Generally, when a function is declared with one signature and called via a function pointer with
a different signature, the two signatures must be ABI-compatible or else calling the function
via that function pointer is Undefined Behavior. ABI compatibility is a lot stricter than merely
having the same memory layout; for example, even if i32
and f32
have the same size and
alignment, they might be passed in different registers and hence not be ABI-compatible.
ABI compatibility as a concern only arises in code that alters the type of function pointers,
code that imports functions via extern
blocks, and in code that combines #[target_feature]
with extern fn
. Altering the type of function pointers is wildly unsafe (as in, a lot more
unsafe than even transmute_copy
), and should only occur in the most
exceptional circumstances. Most Rust code just imports functions via use
. #[target_feature]
is also used rarely. So, most likely you do not have to worry about ABI compatibility.
But assuming such circumstances, what are the rules? For this section, we are only considering
the ABI of direct Rust-to-Rust calls, not linking in general – once functions are imported via
extern
blocks, there are more things to consider that we do not go into here.
For two signatures to be considered ABI-compatible, they must use a compatible ABI string,
must take the same number of arguments, the individual argument types and the return types must
be ABI-compatible, and the target feature requirements must be met (see the subsection below for
the last point). The ABI string is declared via extern "ABI" fn(...) -> ...
; note that
fn name(...) -> ...
implicitly uses the "Rust"
ABI string and extern fn name(...) -> ...
implicitly uses the "C"
ABI string.
The ABI strings are guaranteed to be compatible if they are the same, or if the caller ABI
string is $X-unwind
and the callee ABI string is $X
, where $X
is one of the following:
“C”, “aapcs”, “fastcall”, “stdcall”, “system”, “sysv64”, “thiscall”, “vectorcall”, “win64”.
The following types are guaranteed to be ABI-compatible:
*const T
,*mut T
,&T
,&mut T
,Box<T>
(specifically, onlyBox<T, Global>
), andNonNull<T>
are all ABI-compatible with each other for allT
. They are also ABI-compatible with each other for differentT
if they have the same metadata type (<T as Pointee>::Metadata
).usize
is ABI-compatible with theuN
integer type of the same size, and likewiseisize
is ABI-compatible with theiN
integer type of the same size.char
is ABI-compatible withu32
.- Any two
fn
(function pointer) types are ABI-compatible with each other if they have the same ABI string or the ABI string only differs in a trailing-unwind
, independent of the rest of their signature. (This means you can passfn()
to a function expectingfn(i32)
, and the call will be valid ABI-wise. The callee receives the result of transmuting the function pointer fromfn()
tofn(i32)
; that transmutation is itself a well-defined operation, it’s just almost certainly UB to later call that function pointer.) - Any two types with size 0 and alignment 1 are ABI-compatible.
- A
repr(transparent)
typeT
is ABI-compatible with its unique non-trivial field, i.e., the unique field that doesn’t have size 0 and alignment 1 (if there is such a field). i32
is ABI-compatible withNonZero<i32>
, and similar for all other integer types.- If
T
is guaranteed to be subject to the null pointer optimization, thenT
andOption<T>
are ABI-compatible.
Furthermore, ABI compatibility satisfies the following general properties:
- Every type is ABI-compatible with itself.
- If
T1
andT2
are ABI-compatible andT2
andT3
are ABI-compatible, then so areT1
andT3
(i.e., ABI-compatibility is transitive). - If
T1
andT2
are ABI-compatible, then so areT2
andT1
(i.e., ABI-compatibility is symmetric).
More signatures can be ABI-compatible on specific targets, but that should not be relied upon since it is not portable and not a stable guarantee.
Noteworthy cases of types not being ABI-compatible in general are:
bool
vsu8
,i32
vsu32
,char
vsi32
: on some targets, the calling conventions for these types differ in terms of what they guarantee for the remaining bits in the register that are not used by the value.i32
vsf32
are not compatible either, as has already been mentioned above.struct Foo(u32)
andu32
are not compatible (withoutrepr(transparent)
) since structs are aggregate types and often passed in a different way than primitives likei32
.
Note that these rules describe when two completely known types are ABI-compatible. When
considering ABI compatibility of a type declared in another crate (including the standard
library), consider that any type that has a private field or the #[non_exhaustive]
attribute
may change its layout as a non-breaking update unless documented otherwise – so for instance,
even if such a type is a 1-ZST or repr(transparent)
right now, this might change with any
library version bump.
If the declared signature and the signature of the function pointer are ABI-compatible, then the
function call behaves as if every argument was transmute
d from the
type in the function pointer to the type at the function declaration, and the return value is
transmute
d from the type in the declaration to the type in the
pointer. All the usual caveats and concerns around transmutation apply; for instance, if the
function expects a NonZero<i32>
and the function pointer uses the ABI-compatible type
Option<NonZero<i32>>
, and the value used for the argument is None
, then this call is Undefined
Behavior since transmuting None::<NonZero<i32>>
to NonZero<i32>
violates the non-zero
requirement.
§Requirements concerning target features
Under some conditions, the signature used by the caller and the callee can be ABI-incompatible
even if the exact same ABI string and types are being used. As an example, the
std::arch::x86_64::__m256
type has a different extern "C"
ABI when the avx
feature is
enabled vs when it is not enabled.
Therefore, to ensure ABI compatibility when code using different target features is combined
(such as via #[target_feature]
), we further require that one of the following conditions is
met:
- The function uses the
"Rust"
ABI string (which is the default withoutextern
). - Caller and callee are using the exact same set of target features. For the callee we consider
the features enabled (via
#[target_feature]
and-C target-feature
/-C target-cpu
) at the declaration site; for the caller we consider the features enabled at the call site. - Neither any argument nor the return value involves a SIMD type (
#[repr(simd)]
) that is not behind a pointer indirection (i.e.,*mut __m256
is fine, but(i32, __m256)
is not).
§Trait implementations
In this documentation the shorthand fn(T₁, T₂, …, Tₙ)
is used to represent non-variadic
function pointers of varying length. Note that this is a convenience notation to avoid
repetitive documentation, not valid Rust syntax.
The following traits are implemented for function pointers with any number of arguments and any ABI.
Note that while this type implements PartialEq
, comparing function pointers is unreliable:
pointers to the same function can compare inequal (because functions are duplicated in multiple
codegen units), and pointers to different functions can compare equal (since identical
functions can be deduplicated within a codegen unit).
In addition, all safe function pointers implement Fn
, FnMut
, and FnOnce
, because
these traits are specially known to the compiler.
Auto Trait Implementations§
impl<Ret, T> Freeze for fn(T₁, T₂, …, Tₙ) -> Ret
impl<Ret, T> RefUnwindSafe for fn(T₁, T₂, …, Tₙ) -> Ret
impl<Ret, T> Send for fn(T₁, T₂, …, Tₙ) -> Ret
impl<Ret, T> Sync for fn(T₁, T₂, …, Tₙ) -> Ret
impl<Ret, T> Unpin for fn(T₁, T₂, …, Tₙ) -> Ret
impl<Ret, T> UnwindSafe for fn(T₁, T₂, …, Tₙ) -> Ret
Blanket Implementations§
source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
source§impl<F> Ord for Fwhere
F: FnPtr,
impl<F> Ord for Fwhere
F: FnPtr,
source§impl<F> PartialOrd for Fwhere
F: FnPtr,
impl<F> PartialOrd for Fwhere
F: FnPtr,
source§impl<F> Pattern for F
impl<F> Pattern for F
source§type Searcher<'a> = CharPredicateSearcher<'a, F>
type Searcher<'a> = CharPredicateSearcher<'a, F>
pattern
#27721)source§fn into_searcher<'a>(self, haystack: &'a str) -> CharPredicateSearcher<'a, F>
fn into_searcher<'a>(self, haystack: &'a str) -> CharPredicateSearcher<'a, F>
pattern
#27721)self
and the haystack
to search in.source§fn is_contained_in<'a>(self, haystack: &'a str) -> bool
fn is_contained_in<'a>(self, haystack: &'a str) -> bool
pattern
#27721)source§fn is_prefix_of<'a>(self, haystack: &'a str) -> bool
fn is_prefix_of<'a>(self, haystack: &'a str) -> bool
pattern
#27721)source§fn strip_prefix_of<'a>(self, haystack: &'a str) -> Option<&'a str>
fn strip_prefix_of<'a>(self, haystack: &'a str) -> Option<&'a str>
pattern
#27721)source§fn is_suffix_of<'a>(self, haystack: &'a str) -> boolwhere
CharPredicateSearcher<'a, F>: ReverseSearcher<'a>,
fn is_suffix_of<'a>(self, haystack: &'a str) -> boolwhere
CharPredicateSearcher<'a, F>: ReverseSearcher<'a>,
pattern
#27721)source§fn strip_suffix_of<'a>(self, haystack: &'a str) -> Option<&'a str>where
CharPredicateSearcher<'a, F>: ReverseSearcher<'a>,
fn strip_suffix_of<'a>(self, haystack: &'a str) -> Option<&'a str>where
CharPredicateSearcher<'a, F>: ReverseSearcher<'a>,
pattern
#27721)