As far as I know, there’s two ways to link native libraries in a Rust package:

You can also pass the linker flags directly to rustc, but that’s a bit too low level for packages.

The attribute approach is the easiest, but it’s also fairly inflexible. Once the attribute is set in the upstream FFI library, dependents downstream have no control over it. This is usually OK for most libraries because the name is fixed and known ahead of time.

However, in cases where the name is not known ahead of time (e.g. platform-dependent libraries), or cases where you want to offer the choice of library to the final application, build scripts offer far more flexibility.

One might be tempted to omit the #[link(…)] attribute in the upstream FFI library, and let the downstream application set the attribute, but this doesn’t work as one might expect. This is due to rust-lang/rust#28605, which causes the linker flags to be ordered like this:

cc downstreamapp -l somenativelib upstreamrustlib

Because the native library appears before the upstream library, symbols in the native library are not available for the upstream library.

Fortunately, in build scripts the custom linking flags are always placed at the end. For convenience, one would often place this in a separate crate that contains nothing but a build script like this:

fn main() {
    println!("cargo:rustc-link-lib=dylib=somenativelib");
}