Email me

Rust errors / Everything else

Rust compiler error · checked with rustc 1.99.0

Rust error E0591: per [RFC 401][rfc401], if you have a function declaration `foo`:

What it meansPer [RFC 401][rfc401], if you have a function declaration foo:

E0591Everything else
Activein rustc 1.99.0
8related errors linked below

The compiler team's explanation

Per RFC 401, if you have a function declaration foo:

struct S;

// For the purposes of this explanation, all of these
// different kinds of `fn` declarations are equivalent:

fn foo(x: S) { /* ... */ }
extern "C" {
    fn foo(x: S);
}
impl S {
    fn foo(self) { /* ... */ }
}

the type of foo is **not** fn(S), as one might expect. Rather, it is a unique, zero-sized marker type written here as typeof(foo). However, typeof(foo) can be _coerced_ to a function pointer fn(S), so you rarely notice this:

let x: fn(S) = foo; // OK, coerces

The reason that this matter is that the type fn(S) is not specific to any particular function: it's a function _pointer_. So calling x() results in a virtual call, whereas foo() is statically dispatched, because the type of foo tells us precisely what function is being called.

As noted above, coercions mean that most code doesn't have to be concerned with this distinction. However, you can tell the difference when using **transmute** to convert a fn item into a fn pointer.

This is sometimes done as part of an FFI:

extern "C" fn foo(userdata: Box<i32>) {
    /* ... */
}

unsafe {
    let f: extern "C" fn(*mut i32) = transmute(foo);
    callback(f);
}

Here, transmute is being used to convert the types of the fn arguments. This pattern is incorrect because the type of foo is a function **item** (typeof(foo)), which is zero-sized, and the target type (fn()) is a function pointer, which is not zero-sized. This pattern should be rewritten. There are a few possible ways to do this:

  • change the original fn declaration to match the expected signature, and do the cast in the fn body (the preferred option)
  • cast the fn item of a fn pointer before calling transmute, as shown here:
let f: extern "C" fn(*mut i32) = transmute(foo as extern "C" fn(_));
let f: extern "C" fn(*mut i32) = transmute(foo as usize); // works too

The same applies to transmutes to *mut fn(), which were observed in practice. Note though that use of this type is generally incorrect. The intention is typically to describe a function pointer, but just fn() alone suffices for that. *mut fn() is a pointer to a fn pointer. (Since these values are typically just passed to C code, however, this rarely makes a difference in practice.)

From rustc --explain E0591 (1.99.0), part of the Rust project, MIT or Apache-2.0. Official page.

Related errors

All 518 Rust error codes · Rust crate data · Glossary

Up next

Often hit nextE0061: this function takes N arguments but M were suppliedA related error from the same family.Read it next →

Did this answer your question?

Get the receipts by email.

What my AI agents tried this week, with the real numbers: what worked, what flopped, what made money. One email when there is something worth reading.

No spam, never sold. Every email has a one-click unsubscribe.

Popular in the shop

All 124 tools →

Browse: Bundles and kitsHome, family and schoolMoney and businessAI toolsSports and fitnessMaking and hobbiesFood and drinkIndie dev and launchTrading and quant

Free: Landing page teardown iPhone app cost calculator LLM API cost calculator Paid online: $49 Launch Check App builds Articles FAQ