Calling Rust from Ruby
I’ve falling in love with Rust over the last year and a half. I like it for a lot of reasons, a few being the compiler, seperation of noun/verb, and it’s deeply integrated testing. However, recently been thinking through ways to introduce it to the team and the technology portfolio at Fooda.
One idea we had, was move some of our computational intensive tasks to Rust given that is one of it’s “sweet spots” as a technology. However, this left us wondering how exactly would we mix it in with Ruby. Specifically, how do we call Rust from within Ruby.
As it turns out, it’s actually fairly easy to call Rust from within Ruby so long as you understand a few simple things, namely
- Rust
- C and ABI
- FFI
With those, and a few lines of code to begin calling Rust from within your Ruby code.
Preparing Rust to be “callable” from Ruby
Ultimately, you don’t need to do a whole lot to make your Rust code available to C.
In fact, here’s a simple hello world
example.
With the code snippet above, you’ll see a couple things different from your typical Rust function. So lets break it down …
#[no_mangle]
This tells the Rust compiler to expose this a global function and don’t mangle it with an internal nameextern "C"
This exposes the function as if it was aC
function via theC ABI
*const libc::c_char
Our function returns aC
“string”
Outside that, our function is a regular Rust function that does it’s thing.
Compile in a slightly different way
Now that your Rust code is exposed prepared properly, we need to ensure
we compile in a manner Ruby can accept, specifically a dynamic library. You can do this two ways, the command line or a Cargo toml file. I’m doing it
through Cargo and a toml
file below
When you execute Cargo build
you will now have a dynamic library .dylib
in your
targets folder.
Including your Rust library in Ruby
If there’s an icky part to this whole process, this is it right here. You’ll need a one-to-one
mapping of the functionality you expose in your Rust dynamic library to your Ruby
application.
Here’s an example of how this is done leveraging ffi
Please note, the path
reference above in the ffi_lib
method call is specific to a sample project I’ve
included below.
Test drive in IRB
At this point you are able to take it for a test drive in irb.
That’s it. I was incredibly surprised at how easy it was. The only thing I don’t like,
is maintaining that one-to-one mapping in via ffi
in Ruby. A small price to pay, I guess.
Complete working example
I’ve put together a complete working example that will allow you from irb to call into
a Rust module. It’s a hello world
but it works.
It’s found here