What follows are the slides from a talk I gave at the PDXRust meetup . This presentation was very live demo example heavy, and I’ve replaced the demos with links to specific tags in the example code’s git repositories.
What the FFI?!Interfacing with Foreign Functions in Rust
Nick Fitzgerald
fitzgeraldnick.com @fitzgen What does FFI mean?Foreign F unction I nterface
Wikipedia :
A foreign function interface is a mechanism by which a program written in one programming language can call routines or make use of services written in another.
Using C libraries in Rust
Using Rust from python or Ruby or NodeJS or …?
Why FFI? Reuse… …code that someone else wrote for you …code you already wrote …code that already works Provide an optimized implementation of that inner loop, with fearless concurrency, and memory safety, without rewriting the rest of your application. Hello, World! <insert hello world demo here> Hello, World: Takeawaysextern "C" { ... } to declare the existence of C functions
FFI is always unsafe
The libc crate has all the C type definitions
Compiling and linking involves finicky bookkeeping, so leverage tooling
Leverage Toolinggcc crate to invoke the C compiler on individual .c files
cmake crate to build larger C libraries with cmake
build.rs to do arbitrary things at compile time (e.g. call make )
More tooling later on…
SnappySnappy is a compression and decompression library
Written in C++
With a C API
Hat tip to the Rust book
We’ll write a small Rust program to compress and decompress files using Snappy
Snappy Step 1: Build and Link Snappy from build.rs <insert snappy step 1 demo here> Snappy Step 2: Declare Extern Functions from Snappy <insert snappy step 2 demo here> Snappy Step 3: Compress and Decompress Files with Raw FFI <insert snappy step 3 demo here> Snappy Step 4: Write a Safe, Rusty Interface to Snappy <insert snappy step 4 demo here> Snappy Step 5: Use bindgen Instead of Writing Extern Blocks by Hand <insert snappy step 5 demo here> Snappy: TakeawaysPut building and linking in build.rs and forget about it
Don’t write extern FFI bindings by hand: automate the process with bindgen
Safe, Rusty wrapper APIs:
Study and learn the library’s invariants Who malloc s which things? Who free s them? Which pointers can be NULL ? When is it valid to use this pointer? For how long? Push those invariants into the type system as much as possible When that is not an option, add a liberal sprinkling of asserts! Safe, Rusty APIs Wrapping FFIstd is full of great examples!
Use the [src] , Luke Safe, Rusty APIs Wrapping FFI Use RAII R esource A cquisition I s I nitialization Constructor makes FFI call that allocates a resource The Drop implementation makes the FFI call to deallocate the resource Example: std::fs::File Safe, Rusty APIs Wrapping FFIstruct Raii { raw: *mut raw_ffi_type, } impl Raii { fn new() -> Raii { Raii { raw: unsafe { ffi_allocate() } } } } impl Drop for Raii { fn drop(&mut self) { unsafe { ffi_deallocate(self.raw) } } }
Safe, Rusty APIs Wrapping FFI{ let resource = Raii::new(); ... resource.do_stuff(); ... // Automatically cleaned up at the end of scope }
Safe, Rusty APIs Wrapping FFI Do not use RAII If calling ffi_deallocate is CRITICAL E.g. not calling it results in memory unsafety Also types that cannot move, but you don’t want to Box Instead, use a closure + catch_unwind + cleanup instead Example: scoped threads Safe, Rusty APIs Wrapping FFIstruct Resource { raw: *mut raw_ffi_type, } impl Resource { fn with<F, T>(f: F) -> T where F: FnOnce(&mut Resource) -> T { let mut r = Resource { raw: unsafe { ffi_allocate() }, }; let res = std::panic::catch_unwind( std::panic::AssertUnwindSafe(|| f(&mut r))); unsafe { ffi_deallocate(r.raw); } match res { Err(e) => std::panic::resume_unwind(e), Ok(t) => t, } } }
Safe, Rusty APIs Wrapping FFIResource::with(|resource| { ... resource.do_stuff(); ... })
Rust from Other Languages Step 1: Build Your Crate as a Shared Library <insert python step 1 demo here> Rust from Other Languages Step 2: Passing String Arguments <insert python step 2 demo here> Rust from Other Languages Step 3: Giving Away Ownership of Resources <insert python step 3 demo here> Rust from Other Languages: Next Steps Next, we would In Python, wrap the Rust FFI in a Pythonic API In Rust, use the cpython crate But, we’re going to stop here Rust from Other Languages: Takeaways Inside Cargo.toml : [lib] name = "whatever" crate-type = ["dylib"] Rust from Other Languages: Takeaways To export structs: #[repr(C)] pub struct MyType { ... } Rust from Other Languages: Takeaways To export functions: #[no_mangle] pub extern fn new_my_type() -> *mut MyType { let result = std::panic::catch_unwind(|| { ... }); match result { Ok(r) => r, Err(_) => std::ptr::null_mut(), } } Using Rust from Xrusty-cheddar crate to generate C header files
cpython crate for Python
ruru and helix crates for Ruby
neon crate for NodeJS
rustler crate for Erlang
And more…
More Rust and FFI Learning Resources