現在、RustでRubyのネイティブ拡張を書く場合、rutieを使うことが一般的だと思われる。(helixやruruは保守されていない。)
しかし、下記の理由で個人的に好ましくなかったため、ライブラリに頼らずRubyのネイティブ拡張を作る方法を調べた。

    • 古いRustコンパイラをサポートするが、MaybeUninit等の、より安全なライブラリが利用されていない。

 

    unsafeに対する価値観が私と異なり、完全に安全とは言えない関数であってもunsafeとマークされていない。

下記のコードは HelloRust モジュールをRust側で定義しているサンプルである。

Windows, Linux で動かす場合は続きを参考。

ソースコードベタ貼り

require __dir__ + "/hello_rust"

if defined?(HelloRust)
  puts "HelloRust is defined"
else
  puts "HelloRust is not defined"
end
use std::ffi;

#[repr(transparent)]
pub struct Value(usize);

#[link(name = "ruby")]
extern "C" {
  fn rb_define_module(name: *const i8) -> Value;
}

fn define_module(name: &ffi::CStr) -> Value {
  unsafe { rb_define_module(name.as_ptr()) }
}

#[export_name = "Init_hello_rust"]
pub extern "C" fn init() {
  let s = ffi::CString::new("HelloRust").unwrap();
  define_module(&s);
}
use std::process::Command;

fn libdir() -> String {
  let output = Command::new("ruby")
    .args(&["-e", "print RbConfig::CONFIG['libdir']"])
    .output()
    .expect("failed run ruby");

  return String::from_utf8(output.stdout).unwrap();
}

fn main() {
  println!("cargo:rustc-link-search={}", libdir());
}
[package]
name = "hello_rust"
version = "0.1.0"
authors = ["irxground"]
edition = "2018"

[lib]
crate-type = ["cdylib"]
.PHONY: build
build: hello_rust.bundle

hello_rust.bundle: cargo_build
    cp -a target/release/libhello_rust.dylib $@

.PHONY: cargo_build
cargo_build:
    cargo build --release

.PHONY: run
run: clean build
    ruby run.rb

.PHONY: clean
clean:
    rm *.bundle
    cargo clean

実行結果

$ make run
rm *.bundle
cargo clean
cargo build --release
   Compiling hello_rust v0.1.0 (/Users/user/work/private/ruby_rust/minimam_rust_example)
    Finished release [optimized] target(s) in 0.75s
cp target/release/libhello_rust.dylib hello_rust.bundle
ruby run.rb
HelloRust is defined
广告
将在 10 秒后关闭
bannerAds