dtolnay / dtolnay/linkme

Proposal: add a `#[disjointed_static]` attribute

Open
#81 2 comments 1 reaction 0 assignees View on GitHub
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.