Skip to main content

Quick Start: Your First Extension

Time: 15 minutes | Difficulty: Beginner | Prerequisites: Installation complete

Let's build a working Ruby gem with a Rust-powered native extension. We'll create a FastStrings gem that showcases Rust's performance and safety for string manipulation.

What We're Building

A FastStrings gem that provides a fast, Unicode-aware string reversal method.

require "fast_strings"

FastStrings.reverse("Hello, world! 🦀")
# => "🦀 !dlrow ,olleH"

Step 1: Create Your Gem

We'll use Bundler to generate the gem skeleton with Rust support.

bundle gem --ext=rust fast_strings
cd fast_strings

This creates a standard gem structure with an ext/fast_strings directory for our Rust code.

Step 2: Write Your Rust Code

Replace the placeholder code in ext/fast_strings/src/lib.rs with our string reversal implementation.

We will use the magnus library for high-level, safe bindings to Ruby, and unicode-segmentation to correctly handle Unicode grapheme clusters.

First, add the dependencies to ext/fast_strings/Cargo.toml:

# ext/fast_strings/Cargo.toml
[package]
name = "fast_strings"
version = "0.1.0"
edition = "2021"

[dependencies]
magnus = "0.7"
unicode-segmentation = "1.10"

[lib]
name = "fast_strings"
crate-type = ["cdylib"]

Now, the Rust code:

// ext/fast_strings/src/lib.rs
use magnus::{function, prelude::*, Error, Ruby};
use unicode_segmentation::UnicodeSegmentation;

/// Reverses a string, correctly handling Unicode grapheme clusters.
fn reverse_string(input: String) -> String {
input.graphemes(true).rev().collect()
}

#[magnus::init]
fn init(ruby: &Ruby) -> Result<(), Error> {
let module = ruby.define_module("FastStrings")?;
module.define_singleton_method("reverse", function!(reverse_string, 1))?;
Ok(())
}

Why this approach?

  • Safety: magnus provides a safe API, avoiding unsafe blocks and ensuring memory safety.
  • Correctness: The unicode-segmentation crate ensures that we correctly reverse strings with complex Unicode characters, like emoji.
  • Maintainability: The code is clean, idiomatic Rust, making it easier to understand and maintain.

Step 3: Build and Test

  1. Install dependencies and compile:

    bundle install
    bundle exec rake compile
  2. Write a test: Create a test file test/test_fast_strings.rb to verify the implementation:

    # test/test_fast_strings.rb
    require "test_helper"

    class TestFastStrings < Minitest::Test
    def test_reverse_simple
    assert_equal "dlrow olleh", FastStrings.reverse("hello world")
    end

    def test_reverse_unicode
    assert_equal "🦀!dlroW ,olleH", FastStrings.reverse("Hello, World!🦀")
    end
    end
  3. Run the tests:

    bundle exec rake test

Step 4: Try It in the Console

bundle exec bin/console
require "fast_strings"

FastStrings.reverse("Hello, World! 🦀")
# => "🦀 !dlroW ,olleH"

What You've Learned

  • You've created a Ruby gem with a Rust native extension.
  • You've written safe, performant, and correct Rust code using magnus.
  • You've learned how to handle Unicode strings properly.
  • You've tested your gem to ensure it works as expected.

This quick-start provides a solid foundation for building your own high-performance, reliable Ruby gems with Rust.