liveBook · Manning

Foreign function interface

As explained in Code Like a Pro in Rust

A foreign function interface is a mechanism that allows code in one programming language to call functions or use data types defined in another language.

A foreign function interface (FFI) lets Rust call functions and use data types defined by non-Rust libraries. It connects Rust to C libraries through explicit declarations, compatible types, linking metadata, and carefully contained unsafe code. section 2.6 section 4.8

A practical Rust-to-C FFI typically requires:

  • Declare external functions inside an extern "C" block using C-compatible types from libc. section 2.6
  • Add a link attribute naming the native library, such as zlib, so rustc links the required symbols. section 2.6
  • Wrap direct C calls in unsafe blocks and expose safer Rust-facing wrapper functions. section 2.6
  • Represent shared C structs with #[repr(C)] and translated libc types rather than assuming ordinary Rust types are compatible. section 4.8
  • Prefer rust-bindgen for larger headers; manually copy, convert, and declare simple interfaces when generation is disproportionate. section 4.8

Wrapping zlib compression

A Rust wrapper can allocate a destination buffer with compressBound, pass raw pointers and lengths to zlib's compress function, set the resulting vector length, and return Vec<u8>. A corresponding uncompress wrapper can round-trip “Hello, world!” and verify equality. section 2.6

type GzFile = *mut GzFileState;
 
#[link(name = "z")]                                                   #1
extern "C" {                                                          #2
    fn gzopen(path: *const c_char, mode: *const c_char) -> GzFile;    #2
    fn gzread(file: GzFile, buf: *mut c_uchar, len: c_uint) -> c_int; #2
    fn gzclose(file: GzFile) -> c_int;                                #2
    fn gzeof(file: GzFile) -> c_int;                                  #2
}
 
fn read_gz_file(name: &str) -> String {
    let mut buffer = [0u8; 0x1000];
    let mut contents = String::new();
    unsafe {
        let c_name = CString::new(name).expect("CString failed");     #3
        let c_mode = CString::new("r").expect("CString failed");
        let file = gzopen(c_name.as_ptr(), c_mode.as_ptr());
        if file.is_null() {
            panic!(
                "Couldn’t read file: {}",
                std::io::Error::last_os_error()
            );
        }
        while gzeof(file) == 0 {
            let bytes_read = gzread(
                file,
                buffer.as_mut_ptr(),
                (buffer.len() - 1) as c_uint,
            );
            let s = std::str::from_utf8(&buffer[..(bytes_read as usize)])
                .unwrap();
            contents.push_str(s);
        }
        gzclose(file);
    }
 
    contents
}

This listing shows a complete C-struct mapping and function interface for opening, reading, and closing a gzip file through zlib. section 4.8

How Code Like a Pro in Rust covers it

How deep it goes
Demonstrates declarations, linking, unsafe calls, wrappers, struct layout, and binding generation without exhaustively covering complex C APIs.
Examples
Uses zlib compression, decompression, gzip-file reading, C headers, and bindings for Python, Ruby, Elixir, Erlang, and Node.js.
What you can do after
Build it from scratch by declaring C interfaces, linking native libraries, mapping compatible types, and wrapping unsafe calls.
How it connects
Links C libraries, C structs, libc types, unsafe Rust, rust-bindgen, native libraries, and language integration tooling.

Also explained in

Compare how 2 books explain foreign function interface →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Code Like a Pro in Rust. The section links point to the parts of the book it draws on.