[RFC] externally definable statics
This is an alternative to https://github.com/rust-lang/rfcs/pull/3632
Tracking:
- https://github.com/rust-lang/rust/issues/125418
This strikes me a lot as a simpler form of "distributed slice" a la dtolnay's linkme.
The extensible nature of such a slice would solve more use cases over setting a single value. An easy to see motivation is for custom benchmark or test harnesses to collect cases together via proc macros. Another use case familiar to me is for PyO3 to support collecting methods from multiple impl blocks annotated with #[pymethods] to expose to Python.
Perhaps that can be mentioned as a future possibility to this RFC?
There is a big difference between this RFC and distributed slices: This RFC is trivially implementable across pretty much all linkers in existence. (excluding the default implementation support) As far as linkers are concerned there isn't really a difference between crate_a referencing a symbol and crate_b implementing it (an externally defined static) and vice versa (a regular static referenced from another crate) (except for cycles with static libraries, for which some linkers require --start-group/--end-group or mentioning the static libraries twice) Distributed slices however require either platform specific mechanisms to either concatenate sections or run static constructors or they need rustc to wrap the linker such that it can generate a definition for the distributed slice based on the crate metadata of all dependencies.
Adding distributed slices as future possibility to this RFC is fine with me.
That is indeed how linkme currently implements them; I was hoping that a first-class language feature may be able to find an alternative implementation pathway instead of reliance on the platform & linker. Perhaps that's wishful thinking.
OK I forgot one thing about #[global_allocator]: the type of the static is not a fixed type but anything that implements GlobalAlloc. Meaning you need either one of the following to reproduce #[global_allocator]
// (1) type the static as `dyn Trait`,
// perhaps the most faithful representation but perhaps requires unsized_locals (rust-lang/rust#48055)
extern static ALLOCATOR: dyn GlobalAlloc;
impl static ALLOCATOR: dyn GlobalAlloc = SimpleAllocator { ... };
// (2) type the static as `&dyn Trait`,
// incurring double reference though.
extern static ALLOCATOR: &dyn GlobalAlloc;
impl static ALLOCATOR: &dyn GlobalAlloc = &SimpleAllocator { ... };
// (3) type the static as `impl Trait`,
// maybe not linker-friendly? and not sure if non-object-safe traits can really be used.
extern static ALLOCATOR: impl GlobalAlloc;
impl static ALLOCATOR: impl GlobalAlloc = SimpleAllocator { ... };
maybe a silly question: is this not similar to weak linkage?
In the case where there is a default, yes weak linkage is a possible implementation strategy. For the case where there is no default, it is no different from a regular extern reference in C. No weak linkage need and should be involved as linking must fail if the function is not provided.
@davidhewitt Distributed slice, what you're talking about is actually an open issue in the testing devex team: https://github.com/rust-lang/testing-devex-team/issues/3. I'm currently (slowly) working towards a proposal for that: https://internals.rust-lang.org/t/global-registration-a-kind-of-pre-rfc/20813/23. I'm afraid it will take a little, cause we'll likely do some experimentation in a T-lang experiment. That's distinct from this issue.
About this rfc: I think I liked the other one slightly better. I think that these externally definable statics are very often going to be functions, and having to use closure syntax for them all the time might be a bit awkward. I guess how Mara phrased it in the alternative RFC: that one might be a bit more ergonomic, and I like that. I like having both functions and statics be available to define externally.
It appears to me that the externally defined statics and functions are generalized form of "associated consts" and "required methods", generalized from trait to crate. The root library crate behaves like a trait to define those items, and the leaf binary crate behaves like a struct to implement such items. Is it better to make these two semantics have similar syntax to make it more consistent?