## Linkme: safe cross-platform linker shenanigans

[<img alt="github" src="https://img.shields.io/badge/github-dtolnay/linkme-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/dtolnay/linkme)
[<img alt="crates.io" src="https://img.shields.io/crates/v/linkme.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/linkme)
[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-linkme-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/linkme)
[<img alt="build status" src="https://img.shields.io/github/actions/workflow/status/dtolnay/linkme/ci.yml?branch=master&style=for-the-badge" height="20">](https://github.com/dtolnay/linkme/actions?query=branch%3Amaster)

| Component | Linux | macOS | Windows | FreeBSD | OpenBSD | illumos | Other...<sup>†</sup> |
|:---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| [Distributed slice] | 💚 | 💚 | 💚 | 💚 | 💚 | 💚 | |

<b><sup>†</sup></b> We welcome PRs adding support for any platforms not listed
here.

[Distributed slice]: #distributed-slice

```toml
[dependencies]
linkme = "0.3"
```

*Supports rustc 1.66+*

<br>

# Distributed slice

A distributed slice is a collection of static elements that are gathered into a
contiguous section of the binary by the linker. Slice elements may be defined
individually from anywhere in the dependency graph of the final binary.

The implementation is based on `link_section` attributes and platform-specific
linker support. It does not involve life-before-main or any other runtime
initialization on any platform. This is a zero-cost safe abstraction that
operates entirely during compilation and linking.

### Declaration

A static distributed slice is declared by writing `#[distributed_slice]` on a
static item whose type is `[T]` for some type `T`.

```rust
use linkme::distributed_slice;

#[distributed_slice]
pub static BENCHMARKS: [fn(&mut Bencher)];
```

### Elements

Slice elements may be registered into a distributed slice by a
`#[distributed_slice(...)]` attribute in which the path to the distributed slice
is given in the parentheses. The initializer is required to be a const
expression.

```rust
use linkme::distributed_slice;
use other_crate::BENCHMARKS;

#[distributed_slice(BENCHMARKS)]
static BENCH_DESERIALIZE: fn(&mut Bencher) = bench_deserialize;

fn bench_deserialize(b: &mut Bencher) {
    /* ... */
}
```

Elements may be defined in the same crate that declares the distributed slice,
or in any downstream crate. Elements across all crates linked into the final
binary will be observed to be present in the slice at runtime.

The distributed slice behaves in all ways like `&'static [T]`.

```rust
fn main() {
    // Iterate the elements.
    for bench in BENCHMARKS {
        /* ... */
    }

    // Index into the elements.
    let first = BENCHMARKS[0];

    // Slice the elements.
    let except_first = &BENCHMARKS[1..];

    // Invoke methods on the underlying slice.
    let len = BENCHMARKS.len();
}
```

The compiler will require that the static element type matches with the element
type of the distributed slice. If the two do not match, the program will not
compile:

```rust
#[distributed_slice(BENCHMARKS)]
static BENCH_WTF: usize = 999;
```

```console
error[E0308]: mismatched types
  --> src/distributed_slice.rs:65:19
   |
17 | static BENCH_WTF: usize = 999;
   |                   ^^^^^ expected fn pointer, found `usize`
   |
   = note: expected fn pointer `fn(&mut other_crate::Bencher)`
                    found type `usize`
```

### Function elements

As a shorthand for the common case of distributed slices containing function
pointers, the distributed\_slice attribute may be applied directly to a function
definition to place a pointer to that function into a distributed slice.

```rust
use linkme::distributed_slice;

#[distributed_slice]
pub static BENCHMARKS: [fn(&mut Bencher)];

// Equivalent to:
//
//    #[distributed_slice(BENCHMARKS)]
//    static _: fn(&mut Bencher) = bench_deserialize;
//
#[distributed_slice(BENCHMARKS)]
fn bench_deserialize(b: &mut Bencher) {
    /* ... */
}
```

<br>

#### License

<sup>
Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
</sup>

<br>

<sub>
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
be dual licensed as above, without any additional terms or conditions.
</sub>
