theme | _class | paginate | marp | backgroundColor |
---|---|---|---|---|
default |
lead |
true |
true |
An intro to writing native Ruby extensions in Rust.
You may be new to Rust, and that's OK! You'll find that Rust has many of the things you love about Ruby.
👋 I'm @ianks, and I work on the liquid-perf
team. We are using Rust + WASM to improve the performance of Shopify's Liquid templating engine.
- Live in Atlanta, GA
- Creator of the
oxidize-rb
open-source org - Maintainer of
rb-sys
- Added Rust support to Bundler / RubyGems
For a native gem, we bypass this mechanism entirely and instead exposes native machine code to Ruby. In our native code, we can use the Ruby C API to interact with the Ruby VM.
#include "hello.h"
VALUE hello(VALUE self) {
return rb_str_new_cstr("hello");
}
void Init_hello(void) {
rb_define_global_function("hello", hello, 0);
}
- Speaking in C, "lingua franca"
- Can compile functions with the C calling conventions
- Align items in memory in a way that C understands.
- Due to Rust's robust C FFI, you can code anything in Rust that you could with C.
- Speed: Rust is fast, comparable to C.
- Memory Safety: Rust is designed to prevent memory errors.
- No GC: Means we don't have to worry about 2 GCs running at the same time.
- Ecosystem: Rust has a large ecosystem of libraries and tools (cargo ~= bundler).
- Familiarity: Rust has many features that Ruby developers will be familiar with.
- Performance: You have identified a performance bottleneck that can't be solved in Ruby.
- Complexity: You have a complex native library in C that would benefit from using Rust enums, structs, and traits (yjit).
- Bindings to Rust: You want to make use of a Rust crate (wasmtime, cssparser, etc).
- Bindings to C: You want to make use of a C library (libxml, libcurl, etc), but want memory safety. You can use a Rust crate to wrap the C library (LLVM/inkwell, geos-rs, etc).
The rumours are true, Rust has a steep learning curve. You will battle with the borrow checker, and fight with the compiler as you learn the language.
- Rust errors typically happen at compile time (rather than segfaulting at runtime)
- Can be productive in Rust without fully groking the borrow checker
- Community is very helpful, with a general sentiment of "we've all been there" towards beginners
- You can do it!
- Launching the
liquid-wasm
production verifier - Things just worked
- Rust gives you a "confidence to ship" that you don't get with C/C++.
-
magnus for to handle Ruby C API bindings.
- Drop into rb-sys for low-level Ruby APIs.
-
cargo for dependency management.
-
Use the rb-sys gem for to make
cargo
work with Ruby (viacreate_rust_makefile
). -
rake-compiler for compiling the extensions (as you would with C)
-
Not so distant future: Support for
cargo
in RubyGems (done, available in Ruby 3.2). -
Not so distant future: Support for
bundle gem --rust
in Bundler (PR open, likely Ruby 3.2).
demos/ext/c
demos/ext/rust_rbsys
demos/ext/rust_magnus
- BONUS: Add support for
contains?
indemos/ext/rust_magnus_geo
for a 🦄