Using a C library

Suppose we want to calculate the tangents of a complex number. The num crate offers this functionality; however, for the purposes of this exercise, we will call the ctanf function from the C library libm instead. This is a collection of mathematical functions implemented in C. The following code defines a complex number as a simple struct.

Warning: This code only works on OS X and Linux, not on Windows.
// code from Chapter 10/code/calling_clibrary.rs: 
#[repr(C)] 
#[derive(Copy, Clone)] 
#[derive(Debug)] 
struct Complex { 
    re: f32, 
    im: f32, 
} 
 
#[link(name = "m")]extern {   fn ctanf(z: Complex) -> Complex;} 
 
fn tan(z: Complex) -> Complex { 
    unsafe { ctanf(z) } 
} 
 
fn main() { 
    let z = Complex { re: -1., im: 1. }; // z is -1 + i 
    let z_tan = tan(z); 
    println!("the tangents of {:?} is {:?}", z, z_tan); 
} 

This program prints out the following:

the tangents of Complex { re: -1, im: 1 } is Complex { re: -0.271753, im: 1.083923 }

The #[derive(Debug)] attribute is necessary because we want to show the number in a {:?} format string. #[derive(Copy, Clone)] is needed because we want to use z in the println! statement, after we have moved it by calling tan(z).The function of #[repr(C)] is to reassure the compiler that the type we are passing to C is foreign-function safe, and it tells rustc to create a struct with the same layout as C.

The signatures of the C functions we want to use must be listed in an extern{} block. The compiler cannot check these signatures, so it is important to specify them accurately to make the correct bindings at runtime. This block can also declare global variables exported by C to be used in Rust. These must be marked with static or static mut, for example, static mut version: libc::c_int.

The extern block must be preceded by a #[link(name = "m")] attribute to link in the libm library. This instructs rustc to link to that native library so that symbols from that library are resolved.

The C call itself must evidently be done inside an unsafe {} block. This block is enveloped inside the tan(z) wrapper function, which only uses Rust types. The wrapper can be exposed as a safe interface, hiding the unsafe calls and type conversions between Rust and C types, especially C pointers. When the C code returns a resource, the Rust code must contain destructors for these values to ensure their memory is released.