From b0acfc6e1385e789c0b94b0b8210daa53cd47849 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Fri, 10 May 2024 11:39:17 +0200 Subject: [PATCH 01/17] Add RFC for externally implementable functions. --- ...0000-externally-implementable-functions.md | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 text/0000-externally-implementable-functions.md diff --git a/text/0000-externally-implementable-functions.md b/text/0000-externally-implementable-functions.md new file mode 100644 index 00000000000..c89f7b904ec --- /dev/null +++ b/text/0000-externally-implementable-functions.md @@ -0,0 +1,137 @@ +- Feature Name: `extern_impl_fn` +- Start Date: 2024-05-10 +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) + +# Summary + +A mechanism for defining a function whose implementation can be defined (or overridden) in another crate. + +Example 1: + +```rust +// core::panic: + +extern impl fn panic_handler(_: &PanicInfo) -> !; + +// user: + +impl fn core::panic::panic_handler(_: &PanicInfo) -> ! { + loop {} +} +``` + +Example 2: + +```rust +// log crate: + +extern impl fn logger() -> Logger { + Logger::default() +} + +// user: + +impl fn log::logger() -> Logger { + Logger::to_stdout().with_colors() +} +``` + +# Motivation + +We have several items in the standard library that are overridable/definable by the user crate. +For example, the (no_std) `panic_handler`, the global allocator for `alloc`, and so on. + +Each of those is a special lang item with its own special handling. +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. + +# Explanation + +A function can be defined as "externally implementable" using `extern impl` as follows: + +```rust +// In crate `x`: + +// Without a body: +extern impl fn a(); + +// With a body: +extern impl fn b() { + println!("default impl"); +} +``` + +Another crate can then provide (or override) the implementation of these functions using `impl fn` syntax (using their path) as follows: + +```rust +// In another crate: + +impl fn x::a() { + println!("my implementation of a"); +} + +impl fn x::b() { + println!("my implementation of b"); +} +``` + +# Details + +## Signature + +It is an error to have a different signature for the `impl fn` item + +## No impl + +It is an error to have no `impl fn` item (in any crate) for an `extern impl fn` item without a body. + +## Duplicates + +It is an error to have multiple `impl fn` items (across all crates) for the same `extern impl fn` item. + +## Visibility + +`extern impl fn` items can have a visibility specifier (like `pub`), which determines who can *call* the function (or create pointers to it, etc.). + +*Implementing* the function can be done by any crate that can name the item. + +# Implementation + +The implementation will be based on the same mechanisms as used today for the `panic_handler` and `#[global_allocator]` features. + +The compiler of the root crate will find the implementation of all externally implementable functions and give an error +if more than one implementation is found for any of them. +If none are found, the result is either an error, or, if the `extern impl fn` has a default body, an implementation +is generated that calls that default body. + +# Drawbacks + +- It encourages globally defined behaviour. + - Counterargument: We are already doing this anyway, both inside the standard library (e.g. panic_handler, allocator) + and outside (e.g. global logger). This just makes it much easier (and safer) to get right. + +# Rationale and alternatives + +- The syntax re-uses existing keywords. Alternatively, we could: + - Use the `override` reserved keyword. + - Add a new (contextual) keyword (e.g. `existential fn`). + - Use an attribute (e.g. `#[extern_impl]`) instead. + +# Prior art + +[RFC 2494 "Existential types with external definition"](https://github.com/rust-lang/rfcs/pull/2492) +has been proposed before, which basically does this for *types*. Doing this for functions (as a start) saves a lot of complexity. + +# Unresolved questions + +- What should the syntax be once we stabilize this? +- How should this work in dynamic libraries? + +# Future possibilities + +- Doing this for `static` items too. +- Using this mechanism in the standard library to make more parts overridable. For example: + - Allowing custom implementations of `panic_out_of_bounds` and `panic_overflowing_add`, etc. + (The Rust for Linux project would make use of this.) + - Allowing overriding `write_to_stdout` and `write_to_stderr`. + (This enables custom testing frameworks to capture output. It is also extremely useful on targets like wasm.) From 13e0a588728cb805e6f6a1bab9ff9221c1f08b96 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Fri, 10 May 2024 13:45:34 +0200 Subject: [PATCH 02/17] Update future possibilities. --- text/0000-externally-implementable-functions.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/text/0000-externally-implementable-functions.md b/text/0000-externally-implementable-functions.md index c89f7b904ec..4a8803708fc 100644 --- a/text/0000-externally-implementable-functions.md +++ b/text/0000-externally-implementable-functions.md @@ -129,9 +129,11 @@ has been proposed before, which basically does this for *types*. Doing this for # Future possibilities -- Doing this for `static` items too. +- Doing this for `static` items too. (Perhaps all items that can appear in an `extern "Rust" { … }` block.) - Using this mechanism in the standard library to make more parts overridable. For example: - Allowing custom implementations of `panic_out_of_bounds` and `panic_overflowing_add`, etc. (The Rust for Linux project would make use of this.) - Allowing overriding `write_to_stdout` and `write_to_stderr`. (This enables custom testing frameworks to capture output. It is also extremely useful on targets like wasm.) +- This could possibly be extended to groups of functions in the form of a `trait` that can be globally implemented. + (E.g. `extern impl AsyncRuntime`, to say that there must be a global implementation of that trait.) From c84a3c89ca069bda0181d32f681c46fcce748c2f Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Fri, 10 May 2024 14:35:22 +0200 Subject: [PATCH 03/17] Update. --- text/0000-externally-implementable-functions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/0000-externally-implementable-functions.md b/text/0000-externally-implementable-functions.md index 4a8803708fc..18d0775abad 100644 --- a/text/0000-externally-implementable-functions.md +++ b/text/0000-externally-implementable-functions.md @@ -130,6 +130,7 @@ has been proposed before, which basically does this for *types*. Doing this for # Future possibilities - Doing this for `static` items too. (Perhaps all items that can appear in an `extern "Rust" { … }` block.) +- Using this for existing overridable global behavior in the standard library, like the panic handler, global allocator, etc. - Using this mechanism in the standard library to make more parts overridable. For example: - Allowing custom implementations of `panic_out_of_bounds` and `panic_overflowing_add`, etc. (The Rust for Linux project would make use of this.) From 364552d868ce3e2c3b9e80ac8d08116646d7d674 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Fri, 10 May 2024 15:40:51 +0200 Subject: [PATCH 04/17] Update text/0000-externally-implementable-functions.md Co-authored-by: Josh Triplett --- text/0000-externally-implementable-functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-externally-implementable-functions.md b/text/0000-externally-implementable-functions.md index 18d0775abad..9980754652b 100644 --- a/text/0000-externally-implementable-functions.md +++ b/text/0000-externally-implementable-functions.md @@ -101,7 +101,7 @@ The implementation will be based on the same mechanisms as used today for the `p The compiler of the root crate will find the implementation of all externally implementable functions and give an error if more than one implementation is found for any of them. -If none are found, the result is either an error, or, if the `extern impl fn` has a default body, an implementation +If none are found, and there are any calls to the `extern impl fn` that have not been dead-code eliminated, the result is either an error, or, if the `extern impl fn` has a default body, an implementation is generated that calls that default body. # Drawbacks From c9a3417a3ae6d821e666c15428ca87304c760708 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Fri, 10 May 2024 15:41:29 +0200 Subject: [PATCH 05/17] Update text/0000-externally-implementable-functions.md Co-authored-by: Josh Triplett --- text/0000-externally-implementable-functions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/0000-externally-implementable-functions.md b/text/0000-externally-implementable-functions.md index 9980754652b..d08648b2147 100644 --- a/text/0000-externally-implementable-functions.md +++ b/text/0000-externally-implementable-functions.md @@ -131,6 +131,7 @@ has been proposed before, which basically does this for *types*. Doing this for - Doing this for `static` items too. (Perhaps all items that can appear in an `extern "Rust" { … }` block.) - Using this for existing overridable global behavior in the standard library, like the panic handler, global allocator, etc. +- We could add a mechanism for arbitrating between multiple provided implementations. For instance, if a crate A depended on B and C, and both B and C provide implementations of an `extern impl fn`, rather than an error, A could provide its own implementation overriding both. - Using this mechanism in the standard library to make more parts overridable. For example: - Allowing custom implementations of `panic_out_of_bounds` and `panic_overflowing_add`, etc. (The Rust for Linux project would make use of this.) From d77a2b9b68f990c70be8b4415aee2129c1a5f654 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Fri, 10 May 2024 15:41:48 +0200 Subject: [PATCH 06/17] Update text/0000-externally-implementable-functions.md Co-authored-by: Josh Triplett --- text/0000-externally-implementable-functions.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/0000-externally-implementable-functions.md b/text/0000-externally-implementable-functions.md index d08648b2147..32bbb4e0419 100644 --- a/text/0000-externally-implementable-functions.md +++ b/text/0000-externally-implementable-functions.md @@ -124,6 +124,8 @@ has been proposed before, which basically does this for *types*. Doing this for # Unresolved questions +- Should we provide a mechanism to set an `extern impl fn` using `=` from an existing `fn` value, rather than writing a body? For instance, `impl fn x::y = a::b;` +- Should we allow some form of subtyping, similarly to how traits allow trait impls to do subtyping? - What should the syntax be once we stabilize this? - How should this work in dynamic libraries? From 95af3f8a058cdb07a5d1c2127d8f169bc3afc231 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Fri, 10 May 2024 15:45:02 +0200 Subject: [PATCH 07/17] Update text/0000-externally-implementable-functions.md Co-authored-by: Josh Triplett --- text/0000-externally-implementable-functions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/0000-externally-implementable-functions.md b/text/0000-externally-implementable-functions.md index 32bbb4e0419..c956a65302c 100644 --- a/text/0000-externally-implementable-functions.md +++ b/text/0000-externally-implementable-functions.md @@ -109,6 +109,7 @@ is generated that calls that default body. - It encourages globally defined behaviour. - Counterargument: We are already doing this anyway, both inside the standard library (e.g. panic_handler, allocator) and outside (e.g. global logger). This just makes it much easier (and safer) to get right. +- This will invite the addition of many hooks to the standard library to modify existing behavior. While we should consider such possibilities, this RFC does not propose that every piece of standard library behavior should be replaceable; std is not purely a dispatch layer for a set of hooks. # Rationale and alternatives From 651fe5259c54b27c82f28564cbaf6c7b15379ba6 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Fri, 10 May 2024 15:46:08 +0200 Subject: [PATCH 08/17] Update. --- text/0000-externally-implementable-functions.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/0000-externally-implementable-functions.md b/text/0000-externally-implementable-functions.md index c956a65302c..267aa335bf9 100644 --- a/text/0000-externally-implementable-functions.md +++ b/text/0000-externally-implementable-functions.md @@ -109,7 +109,8 @@ is generated that calls that default body. - It encourages globally defined behaviour. - Counterargument: We are already doing this anyway, both inside the standard library (e.g. panic_handler, allocator) and outside (e.g. global logger). This just makes it much easier (and safer) to get right. -- This will invite the addition of many hooks to the standard library to modify existing behavior. While we should consider such possibilities, this RFC does not propose that every piece of standard library behavior should be replaceable; std is not purely a dispatch layer for a set of hooks. +- This will invite the addition of many hooks to the standard library to modify existing behavior. + While we should consider such possibilities, this RFC does not propose that every piece of standard library behavior should be replaceable. # Rationale and alternatives From 9be0f4deb5ccb113443829f2b0c0084e3dec7562 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Fri, 10 May 2024 16:06:38 +0200 Subject: [PATCH 09/17] Update text/0000-externally-implementable-functions.md Co-authored-by: Josh Triplett --- text/0000-externally-implementable-functions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/0000-externally-implementable-functions.md b/text/0000-externally-implementable-functions.md index 267aa335bf9..14b547d50f7 100644 --- a/text/0000-externally-implementable-functions.md +++ b/text/0000-externally-implementable-functions.md @@ -130,6 +130,7 @@ has been proposed before, which basically does this for *types*. Doing this for - Should we allow some form of subtyping, similarly to how traits allow trait impls to do subtyping? - What should the syntax be once we stabilize this? - How should this work in dynamic libraries? +- If we do end up designing and providing an `extern impl Trait` feature in addition to `extern impl fn`, should we *only* provide `extern impl Trait`, or is there value in still providing `extern impl fn` as well? This RFC proposes that we should still have `extern impl fn`, for the simpler case, rather than forcing such functions to be wrapped in traits. # Future possibilities From 12d72ee7689669717b65409528b4cbb9bccaf93a Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Fri, 10 May 2024 16:07:20 +0200 Subject: [PATCH 10/17] Update text/0000-externally-implementable-functions.md Co-authored-by: Josh Triplett --- text/0000-externally-implementable-functions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/0000-externally-implementable-functions.md b/text/0000-externally-implementable-functions.md index 14b547d50f7..54821905c34 100644 --- a/text/0000-externally-implementable-functions.md +++ b/text/0000-externally-implementable-functions.md @@ -144,3 +144,4 @@ has been proposed before, which basically does this for *types*. Doing this for (This enables custom testing frameworks to capture output. It is also extremely useful on targets like wasm.) - This could possibly be extended to groups of functions in the form of a `trait` that can be globally implemented. (E.g. `extern impl AsyncRuntime`, to say that there must be a global implementation of that trait.) + - Given an `extern impl Trait` feature, could we provide a compatibility mechanism so that a crate providing an `extern impl fn` can migrate to an `extern impl Trait` in a compatible way, such that crates doing `impl fn` will still be compatible with the new `extern impl Trait`? From 4d5516da06f652491498b0b3c8336d3a83efbf94 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Fri, 10 May 2024 17:10:03 +0200 Subject: [PATCH 11/17] Update. --- text/0000-externally-implementable-functions.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/0000-externally-implementable-functions.md b/text/0000-externally-implementable-functions.md index 54821905c34..2e2bfc50466 100644 --- a/text/0000-externally-implementable-functions.md +++ b/text/0000-externally-implementable-functions.md @@ -101,7 +101,7 @@ The implementation will be based on the same mechanisms as used today for the `p The compiler of the root crate will find the implementation of all externally implementable functions and give an error if more than one implementation is found for any of them. -If none are found, and there are any calls to the `extern impl fn` that have not been dead-code eliminated, the result is either an error, or, if the `extern impl fn` has a default body, an implementation +If none are found, the result is either an error, or—if the `extern impl fn` has a default body—an implementation is generated that calls that default body. # Drawbacks @@ -130,6 +130,7 @@ has been proposed before, which basically does this for *types*. Doing this for - Should we allow some form of subtyping, similarly to how traits allow trait impls to do subtyping? - What should the syntax be once we stabilize this? - How should this work in dynamic libraries? +- Should not having an implementation be an error when the function is never called (after dead code elimination)? - If we do end up designing and providing an `extern impl Trait` feature in addition to `extern impl fn`, should we *only* provide `extern impl Trait`, or is there value in still providing `extern impl fn` as well? This RFC proposes that we should still have `extern impl fn`, for the simpler case, rather than forcing such functions to be wrapped in traits. # Future possibilities From e7644f2f5b182e4bc86824ec014ea347820517a9 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Fri, 10 May 2024 17:14:56 +0200 Subject: [PATCH 12/17] Update. --- text/0000-externally-implementable-functions.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/text/0000-externally-implementable-functions.md b/text/0000-externally-implementable-functions.md index 2e2bfc50466..56e7178581c 100644 --- a/text/0000-externally-implementable-functions.md +++ b/text/0000-externally-implementable-functions.md @@ -79,7 +79,9 @@ impl fn x::b() { ## Signature -It is an error to have a different signature for the `impl fn` item +It is an error to have a different signature for the `impl fn` item. + +(Whether `#[track_caller]` is used or not is considered part of the signature here.) ## No impl From cd3757086a96400bbbbde382015a877d6df84d01 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Fri, 10 May 2024 17:16:45 +0200 Subject: [PATCH 13/17] Update. --- text/0000-externally-implementable-functions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/0000-externally-implementable-functions.md b/text/0000-externally-implementable-functions.md index 56e7178581c..447c35621a8 100644 --- a/text/0000-externally-implementable-functions.md +++ b/text/0000-externally-implementable-functions.md @@ -96,6 +96,7 @@ It is an error to have multiple `impl fn` items (across all crates) for the same `extern impl fn` items can have a visibility specifier (like `pub`), which determines who can *call* the function (or create pointers to it, etc.). *Implementing* the function can be done by any crate that can name the item. +(The `impl fn` item will need to name the item to implement, which could be directly or through an alias/re-export.) # Implementation From deb7ba18537e10245c14a9157f1c8b6acd7b828f Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Mon, 13 May 2024 15:47:46 +0200 Subject: [PATCH 14/17] Update. --- ...0000-externally-implementable-functions.md | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/text/0000-externally-implementable-functions.md b/text/0000-externally-implementable-functions.md index 447c35621a8..609e2356717 100644 --- a/text/0000-externally-implementable-functions.md +++ b/text/0000-externally-implementable-functions.md @@ -16,7 +16,8 @@ extern impl fn panic_handler(_: &PanicInfo) -> !; // user: -impl fn core::panic::panic_handler(_: &PanicInfo) -> ! { +impl fn core::panic::panic_handler(panic_info: &PanicInfo) -> ! { + eprintln!("panic: {panic_info:?}"); loop {} } ``` @@ -83,14 +84,14 @@ It is an error to have a different signature for the `impl fn` item. (Whether `#[track_caller]` is used or not is considered part of the signature here.) -## No impl +## One impl It is an error to have no `impl fn` item (in any crate) for an `extern impl fn` item without a body. -## Duplicates - It is an error to have multiple `impl fn` items (across all crates) for the same `extern impl fn` item. +Note: This means that adding or removing an `impl fn` item is a semver incompatible change. + ## Visibility `extern impl fn` items can have a visibility specifier (like `pub`), which determines who can *call* the function (or create pointers to it, etc.). @@ -117,11 +118,36 @@ is generated that calls that default body. # Rationale and alternatives -- The syntax re-uses existing keywords. Alternatively, we could: +## Syntax + +The syntax re-uses existing keywords. Alternatively, we could: - Use the `override` reserved keyword. - Add a new (contextual) keyword (e.g. `existential fn`). - Use an attribute (e.g. `#[extern_impl]`) instead. +## Functions or statics + +This RFC only proposes externally implementable *functions*. + +An alternative is to only provide externally definable *statics* instead. + +That would be equivalent in power: one can store a function pointer in a static, and one can return a reference to a static from a function ([RFC 3635](https://github.com/rust-lang/rfcs/pull/3635)). + +(Another alternative, of course is to provide both. See future possibilities.) + +## Visibility + +There are two kinds of visibilities to be considered for externally implementable functions: +who can *implement* the function, and who can *call* the function. + +Not allowing the function to be implemented by other crates nullifies the functionality, as the entire point of externally implementable functions is that they can be implemented in another crate. This visibility is therefore always (implicitly) "pub". + +Allowing a more restricted (that is, not `pub`) visibility for *calling* the function can be useful. For example, today's `#[panic_handler]` can be defined by any crate, but can not be called directly. (Only indirectly through `panic!()` and friends.) + +A downside is that it is not possible to allow this "only implementable but not publicly callable" visibility through an alias. + +An alternative could be to use the same visibility for both implementing an calling, which would simply mean that the function (or an alias to it) will always have to be `pub`. + # Prior art [RFC 2494 "Existential types with external definition"](https://github.com/rust-lang/rfcs/pull/2492) @@ -133,11 +159,13 @@ has been proposed before, which basically does this for *types*. Doing this for - Should we allow some form of subtyping, similarly to how traits allow trait impls to do subtyping? - What should the syntax be once we stabilize this? - How should this work in dynamic libraries? +- Should there be a way to specify that implementing the function is unsafe, separately from whether the function itself is unsafe? - Should not having an implementation be an error when the function is never called (after dead code elimination)? - If we do end up designing and providing an `extern impl Trait` feature in addition to `extern impl fn`, should we *only* provide `extern impl Trait`, or is there value in still providing `extern impl fn` as well? This RFC proposes that we should still have `extern impl fn`, for the simpler case, rather than forcing such functions to be wrapped in traits. # Future possibilities +- Adding a syntax to specify an existing function as the impl. E.g. `impl core::panic_handler = my_panic_handler;` - Doing this for `static` items too. (Perhaps all items that can appear in an `extern "Rust" { … }` block.) - Using this for existing overridable global behavior in the standard library, like the panic handler, global allocator, etc. - We could add a mechanism for arbitrating between multiple provided implementations. For instance, if a crate A depended on B and C, and both B and C provide implementations of an `extern impl fn`, rather than an error, A could provide its own implementation overriding both. From 01fbc99c8062768b670e84be9fea940fc0b4850b Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 15 May 2024 17:48:37 +0200 Subject: [PATCH 15/17] Update text/0000-externally-implementable-functions.md Co-authored-by: Josh Triplett --- text/0000-externally-implementable-functions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/0000-externally-implementable-functions.md b/text/0000-externally-implementable-functions.md index 609e2356717..2b3550839ec 100644 --- a/text/0000-externally-implementable-functions.md +++ b/text/0000-externally-implementable-functions.md @@ -162,6 +162,7 @@ has been proposed before, which basically does this for *types*. Doing this for - Should there be a way to specify that implementing the function is unsafe, separately from whether the function itself is unsafe? - Should not having an implementation be an error when the function is never called (after dead code elimination)? - If we do end up designing and providing an `extern impl Trait` feature in addition to `extern impl fn`, should we *only* provide `extern impl Trait`, or is there value in still providing `extern impl fn` as well? This RFC proposes that we should still have `extern impl fn`, for the simpler case, rather than forcing such functions to be wrapped in traits. +- An `extern impl fn` that's marked as `pub(crate)` but is nonetheless pub to *implement* could surprise people. Is there some way we can make this less surprising? Should we require that all `extern impl fn` have `pub` visibility? # Future possibilities From d15cc374d8c2816e72ce2f688042e5f1b0248b45 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 15 May 2024 17:53:09 +0200 Subject: [PATCH 16/17] Update. --- text/0000-externally-implementable-functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-externally-implementable-functions.md b/text/0000-externally-implementable-functions.md index 2b3550839ec..a8e26ab876e 100644 --- a/text/0000-externally-implementable-functions.md +++ b/text/0000-externally-implementable-functions.md @@ -160,7 +160,7 @@ has been proposed before, which basically does this for *types*. Doing this for - What should the syntax be once we stabilize this? - How should this work in dynamic libraries? - Should there be a way to specify that implementing the function is unsafe, separately from whether the function itself is unsafe? -- Should not having an implementation be an error when the function is never called (after dead code elimination)? +- Should not having an implementation be an error when the function is never called? - If we do end up designing and providing an `extern impl Trait` feature in addition to `extern impl fn`, should we *only* provide `extern impl Trait`, or is there value in still providing `extern impl fn` as well? This RFC proposes that we should still have `extern impl fn`, for the simpler case, rather than forcing such functions to be wrapped in traits. - An `extern impl fn` that's marked as `pub(crate)` but is nonetheless pub to *implement* could surprise people. Is there some way we can make this less surprising? Should we require that all `extern impl fn` have `pub` visibility? From 1b56e58eba0970e7d2619be97cb8558fa2746ee2 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 22 May 2024 16:51:02 +0200 Subject: [PATCH 17/17] Add note on cfg(). Co-authored-by: Josh Triplett --- text/0000-externally-implementable-functions.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/0000-externally-implementable-functions.md b/text/0000-externally-implementable-functions.md index a8e26ab876e..354d94bfbc4 100644 --- a/text/0000-externally-implementable-functions.md +++ b/text/0000-externally-implementable-functions.md @@ -148,6 +148,10 @@ A downside is that it is not possible to allow this "only implementable but not An alternative could be to use the same visibility for both implementing an calling, which would simply mean that the function (or an alias to it) will always have to be `pub`. +## Configuration + +An `extern impl fn` may have `#[cfg(...)]` attributes applied to it as usual. For instance, a crate may only provide an `extern impl fn` with a given feature flag enabled, and might then use the same feature flag to conditionally provide make other functions depending on that `extern impl fn`. This is a useful pattern for crates that don't want to provide a default implementation but want to avoid producing a compilation error unless the function is needed. + # Prior art [RFC 2494 "Existential types with external definition"](https://github.com/rust-lang/rfcs/pull/2492)