Slide 1

Slide 1 text

Leveraging Rust with mruby Loving our fellow Windows® users + <3

Slide 2

Slide 2 text

Terence Lee @hone02

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

Austin, TX

Slide 6

Slide 6 text

Austin, TX

Slide 7

Slide 7 text

Leveraging Rust with mruby Loving our fellow Windows® users

Slide 8

Slide 8 text

Agenda ● Ruby on Windows ● Intro to mruby & rust ● mruby + rust ● Future?

Slide 9

Slide 9 text

Ruby on Windows

Slide 10

Slide 10 text

Heroku CLI

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

Python https://www.python.org/downloads/windows/

Slide 14

Slide 14 text

Installing Ruby on Windows ● chruby ○ Requirements: bash >= 3 or zsh ● RVM ○ https://rvm.io/support/faq#does-rvm-work-on-windows-will-it-in-the-future ○ Does RVM work on windows? Will it in the future? ■ NO. If you would like to manage multiple versions of ruby on windows please use pik which is an excellent tool by Gordon Thiesfeld. You can find it on GitHub. There are plans to include windows support in RVM 2.0 => https://www.bountysource.com/fundraisers/489-rvm-2-0. ● ruby-build ○ bash script

Slide 15

Slide 15 text

RubyInstaller

Slide 16

Slide 16 text

Extensions on Windows is Hard

Slide 17

Slide 17 text

Extensions on Windows is Hard Nokogiri

Slide 18

Slide 18 text

Ruby is missing a Windows Ecosystem

Slide 19

Slide 19 text

mruby

Slide 20

Slide 20 text

mruby is the lightweight implementation of the Ruby language complying with part of the ISO standard. mruby can be linked and embedded within your application.

Slide 21

Slide 21 text

Differences with MRI

Slide 22

Slide 22 text

no built in File Socket I/O

Slide 23

Slide 23 text

not threadsafe

Slide 24

Slide 24 text

no thread no fork

Slide 25

Slide 25 text

subset of Ruby 2.1

Slide 26

Slide 26 text

procs blocks DHH freedom patching meta programming literals

Slide 27

Slide 27 text

mrubygems?

Slide 28

Slide 28 text

mrbgem.rake MRuby::Gem::Specification.new('hello') do |spec| spec.license = 'MIT' spec.authors = ['Terence Lee'] spec.summary = 'hello world' spec.bins = ['hello'] spec.add_dependency 'mruby-print', :core => 'mruby-print' spec.add_dependency 'mruby-mtest', :mgem => 'mruby-mtest' spec.add_dependency 'mruby-yaml', :github => 'hone/mruby-yaml' end

Slide 29

Slide 29 text

core mrbgems ├── [4.0K] mruby-array-ext ├── [4.0K] mruby-bin-debugger ├── [4.0K] mruby-bin-mirb ├── [4.0K] mruby-bin-mrbc ├── [4.0K] mruby-bin-mruby ├── [4.0K] mruby-bin-mruby-config ├── [4.0K] mruby-bin-strip ├── [4.0K] mruby-compiler ├── [4.0K] mruby-enumerator

Slide 30

Slide 30 text

mgem list

Slide 31

Slide 31 text

mrbgems & Windows?

Slide 32

Slide 32 text

mruby-socket

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

Still making the same mistakes

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

Rust is a systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety.

Slide 37

Slide 37 text

Enabler

Slide 38

Slide 38 text

issues even for seasoned maintainers C Extensions are Hard

Slide 39

Slide 39 text

If you have successfully compiled a Rust program, it will not segfault at runtime.

Slide 40

Slide 40 text

Ownership

Slide 41

Slide 41 text

Zero Cost Abstractions

Slide 42

Slide 42 text

Zero Cost Abstractions pub fn main() { let array = [1,2,3]; let mut sum = 0; for i in 0..3 { sum += array[i] } println!("{:?}", sum); }

Slide 43

Slide 43 text

Zero Cost Abstractions pub fn main() { let array = [1,2,3]; let sum = array.iter().fold(0, |sum, &val| sum + val); println!("{:?}", sum); }

Slide 44

Slide 44 text

Rust 1.0: Stability as a Deliverable (2014) https://blog.rust-lang.org/2014/10/30/Stability.html 6 week release cycle Code compiles on Rust stable 1.0 should compile with Rust stable 1.x

Slide 45

Slide 45 text

Rust Cross-compilation https://blog.rust-lang.org/2016/05/13/rustup.html rustup - a toolchain manager for Rust. "rustup works the same on Windows as it does on Unix" - rustup README Targets: $ rustup target list | wc -l 36 Including: ● x86_64-apple-darwin (installed) ● x86_64-pc-windows-gnu (installed) ● x86_64-unknown-linux-gnu (default) ● x86_64-unknown-linux-musl (installed)

Slide 46

Slide 46 text

Shipping Rust in Firefox https://hacks.mozilla.org/2016/07/shipping-rust-in-firefox/ "Starting with Firefox 48, Mozilla is shipping its first production Rust code, with more to come!" -Dave Herman, Director of Strategy at Mozilla Research

Slide 47

Slide 47 text

Cargo

Slide 48

Slide 48 text

Cargo.toml [package] name = "hello_world" version = "0.1.0" authors = ["Your Name "] [dependencies] time = "0.1.12" regex = "0.1.41"

Slide 49

Slide 49 text

cargo build $ cargo build Updating registry `https://github.com/rust-lang/crates.io-index` Downloading memchr v0.1.5 Downloading libc v0.1.10 Downloading regex-syntax v0.2.1 Downloading memchr v0.1.5 Downloading aho-corasick v0.3.0 Downloading regex v0.1.41 Compiling memchr v0.1.5 Compiling libc v0.1.10 Compiling regex-syntax v0.2.1 Compiling memchr v0.1.5 Compiling aho-corasick v0.3.0 Compiling regex v0.1.41 Compiling hello_world v0.1.0 (file:///path/to/project/hello_world)

Slide 50

Slide 50 text

Cargo.lock [root] name = "hello_world" version = "0.1.0" dependencies = [ "regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "aho-corasick" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", ]

Slide 51

Slide 51 text

mruby + rust

Slide 52

Slide 52 text

Creating a mrbgem with C

Slide 53

Slide 53 text

├── [4.0K] bintest │ └── [ 437] mruby-hello-world.rb ├── [2.3K] build_config.rb ├── [ 298] mrbgem.rake ├── [4.0K] mrblib │ └── [ 42] mruby-hello-world.rb ├── [1.9K] Rakefile ├── [4.0K] src │ └── [ 363] hello_world_gem.c ├── [4.0K] test │ └── [ 119] test_mruby-hello-world.rb └── [4.0K] tools └── [4.0K] mruby-hello-world └── [ 755] mruby-hello-world.c

Slide 54

Slide 54 text

├── [4.0K] bintest │ └── [ 437] mruby-hello-world.rb ├── [2.3K] build_config.rb ├── [ 298] mrbgem.rake ├── [4.0K] mrblib │ └── [ 42] mruby-hello-world.rb ├── [1.9K] Rakefile ├── [4.0K] src │ └── [ 363] hello_world_gem.c ├── [4.0K] test │ └── [ 119] test_mruby-hello-world.rb └── [4.0K] tools └── [4.0K] mruby-hello-world └── [ 755] mruby-hello-world.c

Slide 55

Slide 55 text

hello_world_gem.c #include "mruby.h" mrb_value c_hello(mrb_state * mrb, mrb_value self) { return mrb_str_new_cstr(mrb, "Hello"); } void mrb_mruby_hello_world_gem_init(mrb_state * mrb) { struct RClass *klass = mrb_define_module(mrb, "Hello"); mrb_define_class_method(mrb, klass, "hello", c_hello, MRB_ARGS_NONE()); } void mrb_mruby_hello_world_gem_final(mrb_state * mrb) { }

Slide 56

Slide 56 text

hello_world_gem.c #include "mruby.h" mrb_value c_hello(mrb_state * mrb, mrb_value self) { return mrb_str_new_cstr(mrb, "Hello"); } void mrb_mruby_hello_world_gem_init(mrb_state * mrb) { struct RClass *klass = mrb_define_module(mrb, "Hello"); mrb_define_class_method(mrb, klass, "hello", c_hello, MRB_ARGS_NONE()); } void mrb_mruby_hello_world_gem_final(mrb_state * mrb) { }

Slide 57

Slide 57 text

hello_world_gem.c #include "mruby.h" mrb_value c_hello(mrb_state * mrb, mrb_value self) { return mrb_str_new_cstr(mrb, "Hello"); } void mrb_mruby_hello_world_gem_init(mrb_state * mrb) { struct RClass *klass = mrb_define_module(mrb, "Hello"); mrb_define_class_method(mrb, klass, "hello", c_hello, MRB_ARGS_NONE()); } void mrb_mruby_hello_world_gem_final(mrb_state * mrb) { }

Slide 58

Slide 58 text

hello_world_gem.c #include "mruby.h" mrb_value c_hello(mrb_state * mrb, mrb_value self) { return mrb_str_new_cstr(mrb, "Hello"); } void mrb_mruby_hello_world_gem_init(mrb_state * mrb) { struct RClass *klass = mrb_define_module(mrb, "Hello"); mrb_define_class_method(mrb, klass, "hello", c_hello, MRB_ARGS_NONE()); } void mrb_mruby_hello_world_gem_final(mrb_state * mrb) { }

Slide 59

Slide 59 text

├── [4.0K] bintest │ └── [ 437] mruby-hello-world.rb ├── [2.3K] build_config.rb ├── [ 298] mrbgem.rake ├── [4.0K] mrblib │ └── [ 42] mruby-hello-world.rb ├── [1.9K] Rakefile ├── [4.0K] src │ └── [ 363] hello_world_gem.c ├── [4.0K] test │ └── [ 119] test_mruby-hello-world.rb └── [4.0K] tools └── [4.0K] mruby-hello-world └── [ 755] mruby-hello-world.c

Slide 60

Slide 60 text

mruby-hello-world.rb def __main__(argv) puts Hello.hello end

Slide 61

Slide 61 text

Rust FFI https://blog.rust-lang.org/2015/04/24/Rust-Once-Run-Everywhere.html > Rust makes it easy to communicate with C APIs without overhead, and to leverage its ownership system to provide much stronger safety guarantees for those APIs at the same time. #[no_mangle] pub extern "C" fn double_input(input: i32) -> i32 { input * 2 }

Slide 62

Slide 62 text

├── [4.0K] bintest │ └── [ 437] mruby-rust-hello-world.rb ├── [2.3K] build_config.rb ├── [2.4K] mrbgem.rake ├── [4.0K] mrblib │ ├── [4.0K] mruby-rust-hello-world │ └── [ 41] mruby-rust-hello-world.rb ├── [1.9K] Rakefile ├── [4.0K] rust │ ├── [ 460] Cargo.lock │ ├── [ 196] Cargo.toml │ └── [4.0K] src │ └── [ 710] lib.rs ├── [4.0K] test │ └── [ 119] test_mruby-rust.rb └── [4.0K] tools └── [4.0K] mruby-rust-hello-world └── [ 759] mruby-rust-hello-world.c

Slide 63

Slide 63 text

lib.rs #[no_mangle] pub extern "C" fn mrb_rust_hello(mrb: *mut mrb_state, selfie: mrb_value) -> mrb_value { unsafe { mrb_str_new_cstr(mrb, cstr!("Hello")) } } #[no_mangle] pub extern "C" fn mrb_mruby_rust_hello_world_gem_init(mrb: *mut mrb_state) { unsafe { let klass = mrb_define_module(mrb, cstr!("Rust")); mrb_define_class_method(mrb, klass, cstr!("hello"), mrb_rust_hello as mrb_func_t, MRB_ARGS_NONE()); } } #[no_mangle] pub extern "C" fn mrb_mruby_rust_hello_world_gem_final(mrb: *mut mrb_state) { }

Slide 64

Slide 64 text

Cargo.toml [package] name = "hello" version = "0.1.0" authors = ["Terence Lee "] [dependencies] mferuby = { git = "https://github.com/hone/mferuby.git" } [lib] crate-type = ["staticlib"]

Slide 65

Slide 65 text

mferuby Rust bindings for the mruby C API

Slide 66

Slide 66 text

libmruby-sys crate ├── [4.0K] crates │ ├── [4.0K] libmruby-sys │ │ ├── [ 150] Cargo.toml │ │ └── [4.0K] src │ │ └── [5.1K] lib.rs │ └── [4.0K] mferuby │ ├── [ 203] Cargo.toml │ └── [4.0K] src │ ├── [ 588] lib.rs │ └── [ 815] mrb.rs low level rust binding to mruby C API

Slide 67

Slide 67 text

crates/libmruby-sys/src/lib.rs extern { #[link_name = "tmrb_nil_value"] pub fn nil() -> mrb_value; #[link_name = "tmrb_true_value"] pub fn mrb_true() -> mrb_value; #[link_name = "tmrb_false_value"] pub fn mrb_false() -> mrb_value; #[link_name = "tmrb_fixnum_value"] pub fn fixnum(i: c_int) -> mrb_value; #[link_name = "tmrb_float_value"] pub fn float(mrb: *mut mrb_state, f: c_double) -> mrb_value; pub fn mrb_open() -> *mut mrb_state; pub fn mrb_define_module(mrb: *mut mrb_state, name: *const c_char) -> *mut RClass; pub fn mrb_define_class(mrb: *mut mrb_state, name: *const c_char, super_class: *mut RClass) -> *mut RClass; pub fn mrb_define_class_under(mrb: *mut mrb_state, outer: *mut RClass, name: *const c_char, super_class: *mut RClass) -> *mut

Slide 68

Slide 68 text

mferuby crate ├── [4.0K] crates │ ├── [4.0K] libmruby-sys │ │ ├── [ 150] Cargo.toml │ │ └── [4.0K] src │ │ └── [5.1K] lib.rs │ └── [4.0K] mferuby │ ├── [ 203] Cargo.toml │ └── [4.0K] src │ ├── [ 588] lib.rs │ └── [ 815] mrb.rs high level rust API on top of libmruby-sys

Slide 69

Slide 69 text

crates/mferuby/src/lib.rs pub fn mruby_str_to_rust_string(mruby_string: sys::mrb_value) -> Result { let size = unsafe { sys::RSTRING_LEN(mruby_string) }; let ptr = unsafe { sys::RSTRING_PTR(mruby_string) }; let slice = unsafe { std::slice::from_raw_parts(ptr as *const u8, size as usize) }; let s = try!(std::str::from_utf8(slice)); Ok(s.to_string()) }

Slide 70

Slide 70 text

mferuby-runtime mrbgem ├── [2.3K] build_config.rb ├── [ 412] mrbgem.rake ├── [1.9K] Rakefile ├── [4.0K] src │ ├── [1.4K] mferuby_runtime.c │ ├── [ 138] mferuby_runtime_gem.c │ └── [ 37] mruby.rs └── [4.0K] test └── [ 131] test_mferuby-runtime.rb compiles against mruby and exposes non-public C API to rust

Slide 71

Slide 71 text

src/mferuby_runtime.c #include #include mrb_value tmrb_float_value(struct mrb_state *mrb, mrb_float f) { return mrb_float_value(mrb, f); } mrb_value tmrb_fixnum_value(mrb_int i) { return mrb_fixnum_value(i); } mrb_value tmrb_nil_value() { return mrb_nil_value(); } mrb_value tmrb_true_value() { return mrb_true_value(); }

Slide 72

Slide 72 text

mruby-docopt

Slide 73

Slide 73 text

cargo help

Slide 74

Slide 74 text

CPP, still Hard

Slide 75

Slide 75 text

class CommandLine USAGE = < ruby-util detect-gem ruby-util install-ruby Options: -h --help Show this message USAGE def run(argv) options = Docopt.parse(USAGE, argv) if options["detect-ruby"] puts CLI::DetectRuby.new(File.read(options[""])).ruby_ver sion

Slide 76

Slide 76 text

mruby-docopt pub extern "C" fn parse(mrb: *mut sys::mrb_state, this: sys::mrb_value) -> sys::mrb_value { let mrb_obj = mferuby::Mrb::new(mrb); let mut usage: sys::mrb_value = unsafe { mem::uninitialized() }; let mut argv: sys::mrb_value = unsafe { mem::uninitialized() }; unsafe { sys::mrb_get_args(mrb, cstr!("SA"), &mut usage, &mut argv); } let rust_usage = mferuby::mruby_str_to_rust_string(usage).unwrap(); let argc = unsafe { sys::RARRAY_LEN(argv) }; let mut vec_args: Vec = vec![]; for i in 0..argc { let element = unsafe { sys::mrb_ary_ref(mrb, argv, i) }; vec_args.push(mferuby::mruby_str_to_rust_string(element).unwrap()) ;

Slide 77

Slide 77 text

mruby-docopt pub extern "C" fn parse(mrb: *mut sys::mrb_state, this: sys::mrb_value) -> sys::mrb_value { let mrb_obj = mferuby::Mrb::new(mrb); let mut usage: sys::mrb_value = unsafe { mem::uninitialized() }; let mut argv: sys::mrb_value = unsafe { mem::uninitialized() }; unsafe { sys::mrb_get_args(mrb, cstr!("SA"), &mut usage, &mut argv); } let rust_usage = mferuby::mruby_str_to_rust_string(usage).unwrap(); let argc = unsafe { sys::RARRAY_LEN(argv) }; let mut vec_args: Vec = vec![]; for i in 0..argc { let element = unsafe { sys::mrb_ary_ref(mrb, argv, i) }; vec_args.push(mferuby::mruby_str_to_rust_string(element).unwrap()) ;

Slide 78

Slide 78 text

mruby-docopt pub extern "C" fn parse(mrb: *mut sys::mrb_state, this: sys::mrb_value) -> sys::mrb_value { let mrb_obj = mferuby::Mrb::new(mrb); let mut usage: sys::mrb_value = unsafe { mem::uninitialized() }; let mut argv: sys::mrb_value = unsafe { mem::uninitialized() }; unsafe { sys::mrb_get_args(mrb, cstr!("SA"), &mut usage, &mut argv); } let rust_usage = mferuby::mruby_str_to_rust_string(usage).unwrap(); let argc = unsafe { sys::RARRAY_LEN(argv) }; let mut vec_args: Vec = vec![]; for i in 0..argc { let element = unsafe { sys::mrb_ary_ref(mrb, argv, i) }; vec_args.push(mferuby::mruby_str_to_rust_string(element).unwrap()) ;

Slide 79

Slide 79 text

let result = docopt::Docopt::new(rust_usage) .and_then(|d| d.help(false).argv(vec_args.into_iter()).parse()); match result { Ok(args) => { let args = Box::new(args); let klass = unsafe { sys::mrb_class_get_under(mrb, sys::mrb_module_get(mrb, cstr!("Docopt")), cstr!("Options")) }; unsafe { sys::mrb_obj_value(mrb_obj.data_object_alloc::(kl ass, args, &docopt_option_type)) } }, Err(e) => unsafe { println!("ERROR: {:?}", e); sys::nil() }, } }

Slide 80

Slide 80 text

let result = docopt::Docopt::new(rust_usage) .and_then(|d| d.help(false).argv(vec_args.into_iter()).parse()); match result { Ok(args) => { let args = Box::new(args); let klass = unsafe { sys::mrb_class_get_under(mrb, sys::mrb_module_get(mrb, cstr!("Docopt")), cstr!("Options")) }; unsafe { sys::mrb_obj_value(mrb_obj.data_object_alloc::(kl ass, args, &docopt_option_type)) } }, Err(e) => unsafe { println!("ERROR: {:?}", e); sys::nil() }, } }

Slide 81

Slide 81 text

let result = docopt::Docopt::new(rust_usage) .and_then(|d| d.help(false).argv(vec_args.into_iter()).parse()); match result { Ok(args) => { let args = Box::new(args); let klass = unsafe { sys::mrb_class_get_under(mrb, sys::mrb_module_get(mrb, cstr!("Docopt")), cstr!("Options")) }; unsafe { sys::mrb_obj_value(mrb_obj.data_object_alloc::(kl ass, args, &docopt_option_type)) } }, Err(e) => unsafe { println!("ERROR: {:?}", e); sys::nil() }, } }

Slide 82

Slide 82 text

class CommandLine USAGE = < ruby-util detect-gem ruby-util install-ruby Options: -h --help Show this message USAGE def run(argv) options = Docopt.parse(USAGE, argv) if options["detect-ruby"] puts CLI::DetectRuby.new(File.read(options[""])).ruby_ver sion

Slide 83

Slide 83 text

mruby-docopt in the wild --- a/mrbgem.rake +++ b/mrbgem.rake @@ -12,6 +12,7 @@ MRuby::Gem::Specification.new('heroku-docker-ruby-util') do |spec| spec.add_dependency 'mruby-stringio', github: 'ksss/mruby-stringio' spec.add_dependency 'mruby-process', github: 'hone/mruby-process', branch: 'header' spec.add_dependency 'mruby-io', github: 'hone/mruby-io', branch: 'popen_status' + spec.add_dependency 'mruby-docopt', github: 'hone/mruby-docopt', branch: 'rust' # test deps spec.add_dependency 'mruby-time', core: 'mruby-time'

Slide 84

Slide 84 text

What's Next?

Slide 85

Slide 85 text

mferuby ● Cover more mruby C API with Rust binding ● Create higher level abstractions ● Using mferuby shouldn't require "unsafe"

Slide 86

Slide 86 text

mrbgems around low level rust crates

Slide 87

Slide 87 text

tokio.rs

Slide 88

Slide 88 text

File I/O

Slide 89

Slide 89 text

Integrate with Helix

Slide 90

Slide 90 text

Ruby hasn't had a good Windows history

Slide 91

Slide 91 text

mruby doesn't have to repeat this

Slide 92

Slide 92 text

mruby+rust can create a Windows friendly ecosystem

Slide 93

Slide 93 text

Let's make ruby better for everyone

Slide 94

Slide 94 text

Thank You @hone02 https://github.com/hone/mferuby https://github.com/hone/mruby-docopt