Skip to content

Instantly share code, notes, and snippets.

@briankung

briankung/000.sh Secret

Last active February 3, 2022 11:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save briankung/d250368e9552dc730c6608727556197d to your computer and use it in GitHub Desktop.
Save briankung/d250368e9552dc730c6608727556197d to your computer and use it in GitHub Desktop.
sneak-preview-writing-ruby-gem-native-extensions-in-rust
$ git clone --branch cargo-builder git@github.com:ianks/rubygems.git
Cloning into 'rubygems'...
remote: Enumerating objects: 224785, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 224785 (delta 3), reused 3 (delta 3), pack-reused 224780
Receiving objects: 100% (224785/224785), 191.82 MiB | 38.18 MiB/s, done.
Resolving deltas: 100% (124370/124370), done.
$ cd rubygems
export RUBYGEMS_PATH="$(pwd)"
alias gem="ruby -I$RUBYGEMS_PATH/lib $RUBYGEMS_PATH/bin/gem"
alias bundle="ruby $RUBYGEMS_PATH/bundler/spec/support/bundle.rb"
$ gem --version
3.4.0.dev
$ bundler --version
Bundler version 2.4.0.dev
$ git clone https://github.com/briankung/rust_ruby_example
$ cd rust_ruby_example
$ gem build rust_ruby_example.gemspec --output rust_ruby_example.gem
WARNING: licenses is empty, but is recommended. Use a license identifier from
http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.
WARNING: no homepage specified
WARNING: See https://guides.rubygems.org/specification-reference/ for help
Successfully built RubyGem
Name: rust_ruby_example
Version: 0.1.0
File: rust_ruby_example.gem
$ gem install rust_ruby_example.gem
Building native extensions. This could take a while...
Successfully installed rust_ruby_example-0.1.0
1 gem installed
$ gem install rust_ruby_example.gem
Building native extensions. This could take a while...
ERROR: Error installing rust_ruby_example.gem:
ERROR: Failed to build gem native extension.
current directory: /Users/brian/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/rust_ruby_example-0.1.0
cargo rustc --target-dir /Users/brian/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/extensions/x86_64-darwin-21/3.0.0/rust_ruby_example-0.1.0 --manifest-path /Users/brian/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/rust_ruby_example-0.1.0/Cargo.toml --lib --release --locked -- -C linker\=clang -C link-arg\=-fdeclspec -L native\=/Users/brian/.rbenv/versions/3.0.0/lib -L native\=/Users/brian/.rbenv/versions/3.0.0/lib -L native\=/usr/local/opt/icu4c/lib -C link_arg\=-Wl,-undefined,dynamic_lookup -C link_arg\=-Wl,-multiply_defined,suppress -C debuginfo\=1
cargo failedNo such file or directory - cargo
Gem files will remain installed in /Users/brian/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/rust_ruby_example-0.1.0 for inspection.
Results logged to /Users/brian/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/extensions/x86_64-darwin-21/3.0.0/rust_ruby_example-0.1.0/gem_make.out
$ gem install rust_ruby_example.gem
Building native extensions. This could take a while...
ERROR: Error installing rust_ruby_example.gem:
ERROR: Failed to build gem native extension.
No builder for extension 'Cargo.toml'
Gem files will remain installed in /Library/Ruby/Gems/2.6.0/gems/rust_ruby_example-0.1.0 for inspection.
Results logged to /Library/Ruby/Gems/2.6.0/extensions/universal-darwin-21/2.6.0/rust_ruby_example-0.1.0/gem_make.out
#[no_mangle]
unsafe extern "C" fn pub_reverse(_klass: VALUE, mut input: VALUE) -> VALUE {
let ruby_string = cstr_to_string(rb_string_value_cstr(&mut input));
let reversed = ruby_string.to_string().chars().rev().collect::<String>();
let reversed_cstring = CString::new(reversed).unwrap();
let size = ruby_string.len() as c_long;
rb_utf8_str_new(reversed_cstring.as_ptr(), size)
}
let reversed = ruby_string.to_string().chars().rev().collect::<String>();
let name = CString::new("RustRubyExample").unwrap();
// ...
let klass = unsafe { rb_define_module(name.as_ptr()) };
// Name of the function
let function_name = CString::new("reverse").unwrap();
// transmute the function for unknown reasons
let callback = unsafe {
std::mem::transmute::<
unsafe extern "C" fn(VALUE, VALUE) -> VALUE,
unsafe extern "C" fn() -> VALUE,
>(pub_reverse)
};
// ...Bind the transmuted function as a module function on the RustRubyExample module
unsafe { rb_define_module_function(klass, function_name.as_ptr(), Some(callback), 1) }
$ gem install rust_ruby_example.gem
Building native extensions. This could take a while...
Successfully installed rust_ruby_example-0.1.0
1 gem installed
$ irb
irb(main):001:0' require 'rust_ruby_example'
=> true
irb(main):002:0> RustRubyExample.reverse("rust_ruby_example")
=> "elpmaxe_ybur_tsur"
$ irb
irb(main):001:0' require 'rust_ruby_example'
=> true
irb(main):002:0> RustRubyExample.lowercase("RustRubyExample")
=> "rustrubyexample"
$ irb
irb(main):001:0> require 'rust_ruby_example'
=> true
irb(main):002:0> RustRubyExample.lowercase("RustRubyExample")
Traceback (most recent call last):
4: from /Users/brian/.rbenv/versions/3.0.0/bin/irb:23:in `<main>'
3: from /Users/brian/.rbenv/versions/3.0.0/bin/irb:23:in `load'
2: from /Users/brian/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.0/exe/irb:11:in `<top (required)>'
1: from (irb):2:in `<main>'
NoMethodError (undefined method `lowercase' for RustRubyExample:Module)
// in src/lib.rs
#[no_mangle]
// in src/lib.rs
#[no_mangle]
unsafe extern "C" fn pub_lowercase(_klass: VALUE, mut input: VALUE) -> VALUE {
// ...
}
// in src/lib.rs
#[no_mangle]
unsafe extern "C" fn pub_lowercase(_klass: VALUE, mut input: VALUE) -> VALUE {
let ruby_string = cstr_to_string(rb_string_value_cstr(&mut input));
let lowercased = ruby_string.to_lowercase();
// ...
}
// in src/lib.rs
#[no_mangle]
unsafe extern "C" fn pub_lowercase(_klass: VALUE, mut input: VALUE) -> VALUE {
let ruby_string = cstr_to_string(rb_string_value_cstr(&mut input));
let lowercased = ruby_string.to_lowercase();
let lowercased_cstring = CString::new(lowercased).unwrap();
let size = ruby_string.len() as c_long;
rb_utf8_str_new(lowercased_cstring.as_ptr(), size)
}
// in src/lib.rs
#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn Init_rust_ruby_example() {
let name = CString::new("RustRubyExample").unwrap();
// ...Code for defining "RustRubyExample#reverse" omitted
let function_name = CString::new("lowercase").unwrap();
let callback = unsafe {
std::mem::transmute::<
unsafe extern "C" fn(VALUE, VALUE) -> VALUE,
unsafe extern "C" fn() -> VALUE,
>(pub_lowercase)
};
let klass = unsafe { rb_define_module(name.as_ptr()) };
unsafe { rb_define_module_function(klass, function_name.as_ptr(), Some(callback), 1) }
}
$ gem build rust_ruby_example.gemspec --output rust_ruby_example.gem
// Warnings omitted
Successfully built RubyGem
Name: rust_ruby_example
Version: 0.1.0
File: rust_ruby_example.gem
$ gem install rust_ruby_example.gem
Building native extensions. This could take a while...
Successfully installed rust_ruby_example-0.1.0
1 gem installed
$ irb
irb(main):001:0> require 'rust_ruby_example'
=> true
irb(main):002:1* RustRubyExample.lowercase("RustRubyExample")
=> "rustrubyexample"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment