What the FFI?!
Interfacing with Foreign Functions in Rust
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 Function Interface
-
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!
Hello, World: Takeaways
-
extern "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 Tooling
-
gcc
crate to invoke the C compiler on individual .c files -
cmake
crate to build larger C libraries withcmake
-
build.rs
to do arbitrary things at compile time (e.g. callmake
) -
More tooling later on…
Snappy
-
Snappy 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
Snappy Step 2: Declare Extern Functions from Snappy
Snappy Step 3: Compress and Decompress Files with Raw FFI
Snappy Step 4: Write a Safe, Rusty Interface to Snappy
Snappy Step 5: Use bindgen
Instead of Writing Extern Blocks by Hand
Snappy: Takeaways
-
Put building and linking in
build.rs
and forget about it -
Don’t write
extern
FFI bindings by hand: automate the process withbindgen
-
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?
- Who
- Push those invariants into the type system as much as possible
- When that is not an option, add a liberal sprinkling of asserts!
- Study and learn the library’s invariants
Safe, Rusty APIs Wrapping FFI
-
std
is full of great examples! -
Use the
[src]
, Luke
Safe, Rusty APIs Wrapping FFI
- Use RAII
- Resource Acquisition Is Initialization
- 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 FFI
Safe, Rusty APIs Wrapping FFI
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
- If calling
Safe, Rusty APIs Wrapping FFI
Safe, Rusty APIs Wrapping FFI
Rust from Other Languages Step 1: Build Your Crate as a Shared Library
Rust from Other Languages Step 2: Passing String Arguments
Rust from Other Languages Step 3: Giving Away Ownership of Resources
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
:
Rust from Other Languages: Takeaways
- To export structs:
Rust from Other Languages: Takeaways
- To export functions:
Using Rust from X
-
rusty-cheddar
crate to generate C header files -
cpython
crate for Python -
ruru
andhelix
crates for Ruby -
neon
crate for NodeJS -
rustler
crate for Erlang -
And more…
More Rust and FFI Learning Resources
fin
-
Thanks!
-
Nick Fitzgerald
fitzgeraldnick.com
@fitzgen