Linking native C libraries

Hi. I have two packages. The first one is a lib that uses bindings for native C libary.

unsafe extern "C" {
    fn add(a: i32, b: i32) -> i32;
}

pub fn safe_add(a: i32, b: i32) -> i32 {
    unsafe { add(a, b) }
}

and the second one is exe that depends on this lib:

fn main() {
    let result = rust_lib::safe_add(2, 3);
    println!("The result of adding 2 and 3 is: {}", result);
}

I don't understand a few things:

  1. Why does the lib compile without any build.rs scripts with cargo build? It looks like the compiler doesn't check that the add function exists somewhere.
  2. The exe compiles only if I add these build scripts:
// lib package
fn main() {
    println!("cargo::rustc-link-lib=add");
}

// exe package
fn main() {
    println!("cargo::rustc-link-search=native=<path/to/native/lib>");
 
}

Why does it work like this? I don't understand why I don't need to specify the library path in the lib's build script and the library name in the exe's build script. Do these build scripts depend on each other?

Rust uses traditional C linkers, and the whole linking system is primitive and full of hacks.

All the linked C symbols live in a shared global namespace, which has no concept of libraries (beyond slight grouping of symbols by .a files), no type safety, no checks whether symbols come from the expected source.

The linker, which is not part of Rust, smushes a bunch of files together until all names are connected to something with a matching name, and wishes you good luck.

What you get in extern "C" in Rust is a leaky abstraction. Rust tries to make it look like defined in a specific library, but all it can do is to make object files with missing symbols for the linker to link. The linker just gets a bunch of files, and all flags from all build scripts, but it has no concept of Rust crates and boundaries between them.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.