Ruby / Rust cooperation (2) Means

Series of articles table of contents

Introduction

There are various levels and methods of linking Ruby and Rust.

Use Rust commands

You might be told, "What's wrong?", But one of the cooperation methods is to call the CLI tool (command in short) made with Rust from Ruby and use it.

From the Ruby side, Kernel. # System and IO.popen Call an external program using .ruby-lang.org/ja/2.7.0/method/IO/s/popen.html).

For example, suppose you have a CSV with a large number of columns and rows, and you want to extract some columns from it and perform processing such as aggregation. A tool called xsv made by Rust is useful for extracting columns.

xsv select name,phone people.csv

If you write, CSV that extracts only the name column and phone column from people.csv will be sent to the standard output. This is the pre-processing.

However, it doesn't matter if it's Rust or not if you just call it as an external command. In this section, I wanted to show that "there is a way to combine a program created completely independently by command call and standard input / output, even if it is called cooperation", but I will not cover it later.

FFI: Via Foreign Function Interface

(Note: As with other sections, I wrote it without understanding it, so there may be mistakes)

FFI is called "other language function interface" in Japanese, and is one of the mechanisms for calling functions (etc.) between different programming languages.

The type of data that can be passed seems to be based on that of C language (?), And there are considerable restrictions. For example, when exchanging integers, it is necessary to clarify the type such as ʻuint8 (unsigned 8-bit integer) or ʻint32 (signed 32-bit integer), and Integer objects that do not fit in that range must be clarified. You can't hand it over. It seems that even Ruby arrays and hashes cannot be exchanged (at least easily). The exchange of character strings is a little troublesome.

I think this is probably the list of available types: Types · ffi/ffi Wiki

It's not difficult to call Rust functions from Ruby via FFI. On the Rust side

#[no_mangle]
pub extern fn hoge(x: f64, y: f64) -> f64 {
  x + y
}

Define and compile the function as follows.

On the Ruby side, we use a gem called ffi. And

require "ffi"

module Hoge
  extend FFI::Library
  ffi_lib "path/to/library/file"
  attach_function :hoge, [:double, :double], :double
end

p Hoge.hoge(2.0, 3.0) # => 5.0

Call it like. Isn't it easy enough to beat?

Later, I will write an article in FFI that calls Rust functions from Ruby.

Helix

Whereas FFI is a general-purpose specification that connects various languages, Helix in this section and Rutie in the next section are only for connecting Ruby and Rust.

You can see the following two on Helix's official website.

Also, the following articles are very useful.

Summary of some ways to call Rust from Ruby-Qiita

The sample on the official site suddenly incorporates it into the Ruby on Rails project, which is not clear to those who are not Rails, but the above site is an example of making a gem and it is easy to understand.

It's amazing that Helix can create Ruby classes with Rust. Are you serious.

On the Rust side, a crate called helix is used. Then you can use the macro ruby!

ruby! {
  class Hoge {
    //Instance method
    def foo(&self, s: String) {
    
    }
    
    //Class method
    def bar(s: String) {
    
    }
  }
}

It seems that you can write like this. Unlike FFI, exchanging character strings is as easy as above.

What's interesting is that Ruby's "String object or nil "and" Float object or nil" values can be exchanged as Rust's ʻOption and ʻOption <f64> (I don't know). ).

Arrays and hashes cannot be exchanged. According to Roadmap, long-term goals

Is up.

A little disappointing is that development seems to be slow (to the eye). As of September 4, 2020

It has become. Also, it is hard to say that the maintenance of the site is sufficient.

I haven't tried Helix yet, but I'd like to do a little more research and write an article.

Rutie

Rutie seems to be pronounced like "rooty". It seems to be an implication name such as tie (connecting) Ru of Ruby and Ru of Rust. Official site here:

Like Helix, Rust can create Ruby classes. You can also make modules. On the Rust side, we use a crate called rutie. For example

module!(Hoge);

mehtods!(
    Hoge,
    _rtself,
    
    fn foo(s: RString) -> RString {
        //And so on
    }
);

It seems that you can define the Hoge module and its singular method foo. (A little more description is needed on the Rust side to make this available in Ruby)

On the Ruby side, we use a gem called rutie (same name as the crate). The code example on the Ruby side is omitted. I plan to write another article later.

In Rutie, you can also use Ruby methods on the Rust side. This is interesting. However, I haven't tried it yet, so I'm not sure what it is.

The latest release of Rutie as of September 4, 2020 is July 2020 (version 0.8.0).

Other

There are various other means.

Already mentioned

Summary of some ways to call Rust from Ruby-Qiita

Also describes the method using WASM (WebAssembly).

By the way, whether it is FFI, Helix or Rutie, there are many restrictions on the types of data that can be exchanged.

Is the method via Apache Arrow also promising for exchanging huge data? The author has no idea about this. However, both Ruby and Rust have libraries that use Arrow.

If the data has a complicated structure and you don't care about the encoding / decoding time, it may be via JSON or MessagePack. Both JSON and MessagePack are easy to use in Ruby and Rust.

Benchmark test (added on 2020-09-18)

I haven't compared the speed of each method myself, but I found a benchmark test site. https://github.com/bbugh/ruby-rust-extension-benchmark (October 2019)

Helix seems to be slower than Rutie in this benchmark test. The cost of communication between Ruby / Rust may be a little high.

Benchmark tests often change their rankings with only minor changes in content and scale, and it may be dangerous to make a decision based on the above tests alone. However, looking at the functionality and the pace of development, I feel that Rutie is the one at the moment if only one of them is investigated and used.

Recommended Posts

Ruby / Rust cooperation (2) Means
Ruby / Rust cooperation (1) Purpose
Ruby / Rust linkage (6) Extraction of morphemes
Ruby / Rust linkage (4) Numerical calculation with Rutie