HOWTO execute Rust code with Ruby

Learn how to compile and execute Rust code with Ruby—useful for optimizing performance-critial sections of your Ruby app, or re-using code already written in Rust without having to port it to Ruby.

Install the helix_runtime gem

Make sure you have the helix_runtime gem installed:

gem install helix_runtime

Check you have a Rust compiler installed

You can check what version of Rust you are running with the following command:

rustc --version

If you don’t have a Rust compiler installed you can get one using rustup, using homebrew (brew install rust), or by downloading one of the standalone installers.

Use Helix to generate a new crate

Use the Helix command line to generate a new crate:

helix crate domain

This will create a number of files in crates/domain, structured so you can include the library within your application, or distribute it as a Rust crate and Ruby gem.

Add Rust dependencies

Rust dependencies are defined in a Cargo.toml file, analagous to a .gemspec file in Ruby. Simply add the name and version of the crate you want to add as a dependency to the section of the file labelled [dependencies]. For example:

crates/domain/Cargo.toml
idna = "0.1.4"

Then use cargo to install the dependencies, similar to using bundler in Ruby:

(in ./crates/domain)
cargo update

For the examples in this guide we’ll use the idna crate, which implements the IDNA domain to ASCII algorithm. You can find other crates on the Rust Package Registry.

Add Rust source code

The next step is to flesh out the Rust code in the source file that Helix generated:

crates/domain/src/lib.rs
#[macro_use]
extern crate helix;
extern crate idna;

ruby! {
    class Domain {
        def to_ascii(domain: String) -> Option<String> {
            let result = idna::domain_to_ascii(&domain);

            return result.ok();
        }
    }
}

Compare this to the generated hello world stub: we’ve added a reference to the idna crate, and implemented a class method to wrap the idna::domain_to_ascii Rust function.

Helix will define the necessary Ruby classes and methods, convert Ruby arguments to their Rust equivalents, and convert Rust return values back to their Ruby equivalents.

Build the Rust code

Rust is a compiled language, so the Rust code and Rust dependencies need to be compiled before they can be called from Ruby. Helix helpfully provides a rake build task:

(in ./crates/domain)
rake build

If you see any errors in the output at this step: go back and fix the Rust code, then re-run the build task until it completes successfully.

Call the Rust code from Ruby

Back to Ruby! To test everything is working you can use this short Ruby script:

require 'domain'

puts Domain.to_ascii('Bücher.tld')

Make sure you are executing the script with bundler if you get a LoadError exception.

Helix will automatically add the library to your Gemfile when you generate the crate. The code you write in Rust can then be used like any other gem in your app: from within the application code, from tests, from irb, from rake tasks and so on.