Skip to content

Commit bfb4071

Browse files
committed
Add RFC for externally implementable functions.
1 parent 55a275c commit bfb4071

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
- Feature Name: `extern_impl_fn`
2+
- Start Date: 2024-05-10
3+
- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000)
4+
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)
5+
6+
# Summary
7+
8+
A mechanism for defining a function whose implementation can be defined (or overridden) in another crate.
9+
10+
Example 1:
11+
12+
```rust
13+
// core::panic:
14+
15+
extern impl fn panic_handler(_: &PanicInfo) -> !;
16+
17+
// user:
18+
19+
impl fn core::panic::panic_handler(_: &PanicInfo) -> ! {
20+
loop {}
21+
}
22+
```
23+
24+
Example 2:
25+
26+
```rust
27+
// log crate:
28+
29+
extern impl fn logger() -> Logger {
30+
Logger::default()
31+
}
32+
33+
// user:
34+
35+
impl fn log::logger() -> Logger {
36+
Logger::to_stdout().with_colors()
37+
}
38+
```
39+
40+
# Motivation
41+
42+
We have several items in the standard library that are overridable/definable by the user crate.
43+
For example, the (no_std) `panic_handler`, the global allocator for `alloc`, and so on.
44+
45+
Each of those is a special lang item with its own special handling.
46+
Having a general mechanism simplifies the language and makes this functionality available for other crates, and potentially for more use cases in core/alloc/std.
47+
48+
# Explanation
49+
50+
A function can be defined as "externally implementable" using `extern impl` as follows:
51+
52+
```rust
53+
// In crate `x`:
54+
55+
// Without a body:
56+
extern impl fn a();
57+
58+
// With a body:
59+
extern impl fn b() {
60+
println!("default impl");
61+
}
62+
```
63+
64+
Another crate can then provide (or override) the implementation of these functions using `impl fn` syntax (using their path) as follows:
65+
66+
```rust
67+
// In another crate:
68+
69+
impl fn x::a() {
70+
println!("my implementation of a");
71+
}
72+
73+
impl fn x::b() {
74+
println!("my implementation of b");
75+
}
76+
```
77+
78+
# Details
79+
80+
## Signature
81+
82+
It is an error to have a different signature for the `impl fn` item
83+
84+
## No impl
85+
86+
It is an error to have no `impl fn` item (in any crate) for an `extern impl fn` item without a body.
87+
88+
## Duplicates
89+
90+
It is an error to have multiple `impl fn` items (across all crates) for the same `extern impl fn` item.
91+
92+
## Visibility
93+
94+
`extern impl fn` items can have a visibility specifier (like `pub`), which determines who can *call* the function (or create pointers to it, etc.).
95+
96+
*Implementing* the function can be done by any crate that name the item.
97+
98+
# Implementation
99+
100+
The implementation will be based on the same mechanism as used today for the `panic_handler` and `#[global_allocator]` features.
101+
102+
The compiler of the root crate will find the implementation of all externally implementable functions and give an error
103+
if more than one implementation is found for any of them.
104+
If none are found, the result is either an error, or, if the `extern impl fn` has a default body, an implementation
105+
is generated that calls that default body.
106+
107+
# Drawbacks
108+
109+
- It encourages globally defined behaviour.
110+
- Counterargument: We are already doing this anyway, both inside the standard library (e.g. panic_handler, allocator)
111+
and outside (e.g. global logger). This just makes it much easier (and safer) to get right.
112+
113+
# Rationale and alternatives
114+
115+
- The syntax re-uses existing keywords. Alternatively, we could:
116+
- Use the `override` reserved keyword.
117+
- Add a new (contextual) keyword (e.g. `existential fn`).
118+
- Use an an attribute (e.g. `#[extern_impl]`) instead.
119+
120+
# Prior art
121+
122+
[RFC 2494 "Existential types with external definition"](https://github.com/rust-lang/rfcs/pull/2492)
123+
has been proposed before, which basically does this for *types*. Doing this for functions (as a start) saves a lot of complexity.
124+
125+
# Unresolved questions
126+
127+
- What should the syntax be once we stabilize this?
128+
- How should this work in dynamic libraries?
129+
130+
# Future possibilities
131+
132+
- Doing this for `static` items too.
133+
- Using this mechanism in the standard library to make more parts overridable.
134+
- E.g. allowing custom implementations of `panic_out_of_bounds` and `panic_overflowing_add`, etc.
135+
(The Rust for Linux project would make use of this.)

0 commit comments

Comments
 (0)