Calling Rust from Ruby: FFI vs C extensions?


#1

Hello, enthusiastic Rust newbie here

I’m trying to get my head around the pros and cons of using FFI (Foreign Function Interface) or C extensions to call Rust from Ruby code.

I’ve seen a few talks, including one by by @steveklabnik about calling Rust from Ruby using C extensions and came across some wrapper libraries such as Helix that help reduce the cognitive overhead induced by the boilerplate code required to create extensions.

I’ve also read about FFI (Foreign Function Interface). The code examples I’ve studied seem much more simple to implement. There seems to be also some advantages with FFIs, such as the ease of installation (no compilation required by the user) and portability to other ruby implementations.

What are the pros of C extensions over FFI? Why do talks from Rust core team members revolve around the former instead of the latter? What did I miss?


#2

Fundamentally, C extensions and FFI boil down to the same thing: Ruby knowing how to call C functions, and Rust knowing how to be called like a C function. In fact, I’m not actually sure what the distinction is that you’re drawing between the two. Could you maybe link to some of these examples you’re talking about?


#3

Sure! Thanks for the quick answer.

Here’s an example using C extensions from the skylight blog (under The Magician Secret section, on the right),

The example use both C and Rust code to achieve a faster #blank? method on strings. I suppose all the boilerplate and the usage of ruby.h lib is necessary since this code extend the String class directly from the extension, when Ruby initializes the extension.

Here is another example using FFI. The example does not achieve the same thing but the simplicity of this example struck me, when compared to the first one.

I guess it all comes down to extending Ruby or simply calling foreign functions from Ruby. Right?


#4

Almost everything. FFI is extremely painful to use. Passing a string between environments becomes a hard problem. Any non-trivial interaction or data sharing is something you will have to think hard about. C extensions allow you to register data on Ruby side, let Ruby manage it, and stop worrying about that. FFI is not something you want to work with unless it is absolutely trivial (which many hello-world demos are, ignoring the fact that any step beyond that means you fall of the cliff).


#5

Ah ha, thank you. So yeah, to refame slightly:

  • “c extensions” to both of us is Ruby’s “c extension” API
  • “FFI” to you us Ruby’s FFI library, but to me, is a general mechanism.

So I see “c extensions” as a form of FFI, and the “FFI” library as a form of FFI. That’s where our differences lie :smile:

I gave my talk on C extensions because I typically use them over the FFI library, but not for any other reason than “that’s just what I’ve always done.” One nice thing in Ruby about it is that rubygems knows how to deal with c extensions, but with FFI, https://github.com/alexcrichton/rust-ffi-examples/blob/master/ruby-to-rust/src/main.rb#L5 <- this bit is on you.

But yeah, other than that, mostly no specific reason.