A Rusting Rubyist

Embedding Rust in a Ruby Module

Mike Piccolo
FullStack Labs

--

This is the first post in a series where I will document my attempt to create a web scraping library in Rust that can be called from a Ruby module. I am a total newb in systems programming so this should be “fun”. The reason for doing a web scraper is because it requires a moderate level of expertise to complete and it has a wide variety of components which will cover plenty of Rust to provide for a good intro. Because I am a Rubyist I plan on making the library embeddable and open up a Ruby API.

I am pushing all my code to: https://github.com/mfpiccolo/scrape

For this post use branch part-1.

Why Rust?

It was at GoGaRuCo 2014 when first hear about Rust. Yehuda Katz’s presentation was quite the intro. He has a way of making packing a ton of information in 35 minutes. Truth be told I didn’t really retain much of it but what I did take away was Yehuda was very excited about it. One thing I have learned so far in my programming career is that when Yehuda gets excited about something, it should go on your radar.

The exciting part about Rust, specifically for Rubyist’s is the prospect of writing low level code. Now, when I first heard I was not enthused on the idea of doing system programming. I had dabbled with some C here and there and basically hated it. The pitch for going from Ruby to C is really not a great one.

“What takes you one line in Ruby, you could write in six lines in C. Not only that, there is a really good chance you destroy the machine you are running it on!”

Yeah. I am not a computer science major so there is really no way for me to feel comfortable programming in that environment.

So why systems programming at all if it is much easier and nicer up in the high level?

Speed and power. Ruby is making tons of decisions for you, for example memory allocation, garbage collection, ect. Each decision it is making for you slows down the program and constrains you to doing it the Ruby way.

Rust to the Rescue (kind of)

Rust takes care of most of the safety issues that most systems languages have. They are very proud of their safety. It is right in their tagline:

Rust is a systems programming language that runs blazingly fast, prevents nearly all segfaults, and guarantees thread safety.

Well fantastic. Enough of the intro lets get going.

Skipping Hello world

Lets get right to it. If you want a step by step follow the book. Steve Klabnik put together an excellent intro. For us building an HTML parser, we need an embeddable lib not a hello world.

Lets grab multirust

$ curl -sf https://raw.githubusercontent.com/brson/multirust/master/blastoff.sh | sh

Now lets make a lib and use the nightly build because fortune favors the bold.

$ cargo new scrape
$ cd scrape
$ multirust override nightly

Lets Code

Awesome. We have a lib.rs file now.

Lets run a test to make sure that it works.

$ cargo test
Compiling scrape v0.1.0 (file:///Users/mfpiccolo/code/mfpiccolo/scrape)
Running target/debug/embed-0c5f308515706b9f
running 1 test
test it_works ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

Now lets try to get something going. How about adding some numbers and returning the sum.

Here we are passing in specific size integer and returning it. Hurray! It is passing.

One thing to note about Rust vs Ruby is that pretty much everything is explicit. In this example you have to tell it the type of each argument (x: i32, y: i32) and what the function is going to return -> i32. The compiler is incredibly picky so you have to get used to it barking at you when you compile. If we were to return a string or any other type it would say something like:

error: mismatched types:
expected `i32`,
found `String`
(expected i32,
found String)

Time for Ruby

It is about time that we get this library working in a Ruby file.

Lets open up our Cargo.toml file and add in some config to help cargo build the executable file.

Now we need to change up the function a little to allow it to be called externally.

We now need to add our ruby module.

$ touch scrape.rb
$ touch Gemfile

Add FFI to our Gemfile

And our FII module

As you can see this just attaches our sum function and describes the types of args and return value just like Rust wants us to do. Everything is so demanding!

I added a cutesy little unit test there to make sure that our embedded function is running.

Run the code:

$ ruby scrape.rb
Gone Dun It!

Fin

That’s all for now. You should be proud of yourself. If you followed along this might be the closest you have gotten to the metal of that macbook pro, all controlled by you!

Next Time

Lets get into something cooler and more applicable to scraping. Lets pass in a string, mutate it in Rust and return it to Ruby! I am predicting the compiler yelling quite a bit before I get through that one.

Part II

Special Thanks

The rust community, for the most part, is pretty nice to newbs so don’t be afraid to ask a Stack Overflow question or get on the rust IRC channel. Special thanks to Stack Overflow users Adrian, shepmaster, Chris Morgan and DK. Also Steve Klabnik for doing a great job on the docs.

--

--

Mike Piccolo
FullStack Labs

JavaScript/Ruby, software developer, business consultant, entrepreneur, outdoorsman. https://fullstacklabs.co/