|
| 1 | +Per [RFC 401][rfc401], if you have a function declaration `foo`: |
| 2 | + |
| 3 | +``` |
| 4 | +// For the purposes of this explanation, all of these |
| 5 | +// different kinds of `fn` declarations are equivalent: |
| 6 | +struct S; |
| 7 | +fn foo(x: S) { /* ... */ } |
| 8 | +extern "C" { fn foo(x: S); } |
| 9 | +impl S { fn foo(self) { /* ... */ } } |
| 10 | +``` |
| 11 | + |
| 12 | +the type of `foo` is **not** `fn(S)`, as one might expect. |
| 13 | +Rather, it is a unique, zero-sized marker type written here as `typeof(foo)`. |
| 14 | +However, `typeof(foo)` can be _coerced_ to a function pointer `fn(S)`, |
| 15 | +so you rarely notice this: |
| 16 | + |
| 17 | +``` |
| 18 | +let x: fn(S) = foo; // OK, coerces |
| 19 | +``` |
| 20 | + |
| 21 | +The reason that this matter is that the type `fn(S)` is not specific to |
| 22 | +any particular function: it's a function _pointer_. So calling `x()` results |
| 23 | +in a virtual call, whereas `foo()` is statically dispatched, because the type |
| 24 | +of `foo` tells us precisely what function is being called. |
| 25 | + |
| 26 | +As noted above, coercions mean that most code doesn't have to be |
| 27 | +concerned with this distinction. However, you can tell the difference |
| 28 | +when using **transmute** to convert a fn item into a fn pointer. |
| 29 | + |
| 30 | +This is sometimes done as part of an FFI: |
| 31 | + |
| 32 | +``` |
| 33 | +extern "C" fn foo(userdata: Box<i32>) { |
| 34 | + /* ... */ |
| 35 | +} |
| 36 | + |
| 37 | +let f: extern "C" fn(*mut i32) = transmute(foo); |
| 38 | +callback(f); |
| 39 | +``` |
| 40 | + |
| 41 | +Here, transmute is being used to convert the types of the fn arguments. |
| 42 | +This pattern is incorrect because, because the type of `foo` is a function |
| 43 | +**item** (`typeof(foo)`), which is zero-sized, and the target type (`fn()`) |
| 44 | +is a function pointer, which is not zero-sized. |
| 45 | +This pattern should be rewritten. There are a few possible ways to do this: |
| 46 | + |
| 47 | +- change the original fn declaration to match the expected signature, |
| 48 | + and do the cast in the fn body (the prefered option) |
| 49 | +- cast the fn item fo a fn pointer before calling transmute, as shown here: |
| 50 | + |
| 51 | + ``` |
| 52 | + let f: extern "C" fn(*mut i32) = transmute(foo as extern "C" fn(_)); |
| 53 | + let f: extern "C" fn(*mut i32) = transmute(foo as usize); // works too |
| 54 | + ``` |
| 55 | + |
| 56 | +The same applies to transmutes to `*mut fn()`, which were observedin practice. |
| 57 | +Note though that use of this type is generally incorrect. |
| 58 | +The intention is typically to describe a function pointer, but just `fn()` |
| 59 | +alone suffices for that. `*mut fn()` is a pointer to a fn pointer. |
| 60 | +(Since these values are typically just passed to C code, however, this rarely |
| 61 | +makes a difference in practice.) |
| 62 | + |
| 63 | +[rfc401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md |
0 commit comments