This year, Godfrey and Yehuda Katz talked again about this topic, announcing the version 0.5.0 of Helix: a bridge between Ruby and Rust. On the Helix website there is documentation that covers getting started with it and Rails, but it lacks of clear examples to build a Ruby gem 😞.

So I looked at the examples trying to figure out how to build a Ruby gem with it. This time I succeeded 🎉, and here's how I did it.

Rewriting Ruby

This year, instead of String#blank? I decided to rewrite a single function (Hanami::Utils::Escape.html) and to package it into a standalone Ruby gem. While blank check returns a simple output (a boolean), HTML escaping allows to experiment with two-way communication between the two languages: a string as input and a string as output.

Setup

First, I generated a new Ruby gem.

$ bundle gem hanami_utils_escape

Helix at this stage doesn't support Ruby modules. In case you want to try, please make sure that the gem name will result into a top-level class (in the example: HanamiUtilsEscape).

Ruby implementation

I had to simplify the new method signature a bit. The original Hanami::Utils::Escape.html returns a Hanami::Utils::Escape::SafeString instance, but for now Helix is only able to return Ruby's String, true, false, Float, Integer, and nil types from a Rust function.

This is limiting and I really hope that in the future they will be able to support at least Array, and Hash too. 🤞

Another limitation to consider is encoding: Helix supports only UTF-8 strings. The caller of the Ruby function must ensure that the input of the function will have that encoding.

Adding Helix

Adding Helix requires some knowledge of how Rust and Cargo (the package manager) work.

I added helix_runtime to hanami_utils_escape.gemspec:

spec.add_dependency"helix_runtime","~> 0.5.0"

At the root of the project, I created the Cargo.toml, which is the Rust equivilent of Bundler's Gemfile:

Running bundle exec rake this time will use the Rust implementation! 🎉

Benchmarks

The main reason why you, as a Rubyist, may be interested in Rust/Helix is its performance. Let's compare the Ruby and Rust implementations with different sized inputs.

For all the samples: strings with 100, 1,000 and 10,000 chars, the Rust implementation is twice as fast as Ruby.

Conclusion

My Rust knowledge is very basic. To make that code to compile required spending a couple of frustrating hours. Most likely it isn't the best implementation, and it can be probably improved.

On the production side, I haven't found a way to package the final gem to be pushed against Rubygems.

The performance characteristics are appealing, but there is too much effort to make it to work and too much trial-and-error in the process. I suggest to use Helix in production for small, focused tests. For the time being, we are not considering to use it on DNSimple.

For the complete implementation of my experiment, please check the source code.