Proposal: add a `#[disjointed_static]` attribute
- Dominant language
- Rust
- Stars
- 890
- Forks
- 46
- PR merge metrics
- No merged PRs in 30d
Description
Declaring a `static` to be defined downstream seems right in the "linker shenanigans" alley of the linkme crate, and can be made (mostly) typesafe using essentially the same linker techniques as with `#[distributed_slice]`.
I'm most interested in the `static RESOURCE: dyn Trait` case (c.f. `#[distributed_slice]` using `static SLICE: [T]`), but any concrete type that can be put in a `static` could of course be used.
If you agree that this would be a reasonable fit for the linkme crate to provide, I'm willing to work on getting a working implementation.
## Example
```rust
// upstream
pub trait Log {
fn record(&self, entry: &Record<'_>);
fn flush(&self);
}
#[global_resource]
pub static GLOBAL_LOGGER: dyn Log;
// downstream
#[global_resource(GLOBAL_LOGGER)]
static LOGGER: MyLogger = MyLogger::new();
```
## Implementation strategy
There are two main potential implementation strategies. The first is to use a `DistributedSlice<&dyn Log>` with a runtime `assert_eq!(len(), 1)` (like the distributed slice dupcheck). Reusing the `#[distibuted_slice]` machinery is a reason for putting this in linkme.
The second one is to use a single symbol instead, e.g.
```rust
// upstream
static GLOBAL_LOGGER: GlobalResource = {
extern {
#[link_name = "__linkme.resource.GLOBAL_LOGGER"]
static LINKME: &dyn Log;
}
GlobalResource::private_new(LINKME, todo!())
};
// downstream
static LOGGER: MyLogger = MyLogger::new();
const _: () = {
#[link_name = "__linkme.resource.GLOBAL_LOGGER"]
static LINKME: &dyn Log = &LOGGER;
};
```
(attributes aren't exactly correct). An advantage of this approach is that it (potentially) works under Miri, but a big disadvantage is that it relies on linker errors for missing or duplicated definition of the linked symbol. Using weak symbols might be able to avoid this.
As a variation, instead of linking `static: &dyn Log`, link `fn() -> &dyn Log` instead. That could be more well supported and allows the downstream to more directly do lazy initialization, which could be an upside or drawback, depending on what you want.
In either case, the ability to provide a default implementation is useful (e.g. how `#[global_allocator]` defaults to as-if `Global` is used) and would theoretically be possible: a strongly ordered slice member for the former implementation strategy, and weak symbol definition for the latter.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by reading linkme's existing #[distributed_slice] machinery and its duplicate-check behavior. Compare the proposed DistributedSlice and single-symbol strategies, including missing, duplicate, default, Miri, and lazy-initialization concerns. Done means a working #[disjointed_static] design with documented semantics and coverage for the proposed upstream/downstream example.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- build-system
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100