Course contentsShow
← Rust
Lesson 1486 of 1,91012. Unsafe Rust and Low-Level AccessFree lesson

Re-exporting C Symbols

You will create wrapper libraries that selectively export functionality from underlying foreign code.

In this lesson, you will learn how to create a Rust library that acts as a curated gateway, selectively exposing underlying C functionality to other systems.

The Bridge Pattern

When working with legacy C libraries, you often face a "surface area" problem: the C library may contain hundreds of functions, global variables, and macros that are either redundant, dangerous, or confusing. Instead of forcing consumers to interact with raw extern "C" blocks, you can create a wrapper crate that acts as a facade.

By re-exporting only specific symbols, you enforce a cleaner API and provide a safer, more idiomatic surface for your users.

Implementing the Export

To re-export C symbols, you use the pub use syntax alongside your extern block. By defining your FFI layer in a private module and selectively exposing it, you maintain control over the public interface.

// In your `ffi.rs` module
mod raw {
    #[link(name = "legacy_engine")]
    extern "C" {
        pub fn init_engine();    // Need this
        pub fn process_data();   // Need this
        pub fn internal_debug(); // Keep this hidden!
    }
}

// In your `lib.rs`, re-export only the safe subset
pub use crate::raw::{init_engine, process_data};

Why Re-export?

  1. Encapsulation: You hide internal C complexity. If the underlying C library changes its function names, you only update the ffi.rs module; your consumer's code remains untouched.
  2. Namespace Control: You can re-export functions into a logical module hierarchy (e.g., my_crate::engine::init) rather than polluting your crate root with snake_case C names.
  3. Documentation: By re-exporting, you can attach Rust documentation attributes (///) to the exported symbols, providing help that the original C header files lack.

When re-exporting, always remember that you are essentially acting as the gatekeeper for the foreign memory. While you are merely routing the symbol, you are conceptually endorsing its safety. Ensure that you wrap these re-exported symbols in unsafe blocks only where necessary, or ideally, wrap them in safe Rust functions that handle the validation of inputs before calling the re-exported C code.

Key Takeaway: Re-exporting C symbols allows you to selectively curate a foreign API, transforming a raw, expansive C interface into a clean, well-documented, and safe Rust library.