Slide 1

Slide 1 text

No content

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

Godfrey Chan @chancancode

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

Motivation 㵕䱛

Slide 9

Slide 9 text

Motivation
 㵕䱛 - Ruby is slow… - Usually it doesn’t matter - Most workload are I/O bound - But occasionally it does…

Slide 10

Slide 10 text

Motivation
 㵕䱛 - “Best of both worlds”: native extensions - JSON gem - Very fast - Transparent to the user - Date, Pathname, etc…

Slide 11

Slide 11 text

Motivation
 㵕䱛 - Example: String#blank? - Sam Saffron’s fast_blank - 50 LOC in C - 20x speedup

Slide 12

Slide 12 text

Motivation
 㵕䱛 - But C… - Unsafe, risky - Maintenance burden - Contribution barrier

Slide 13

Slide 13 text

Motivation
 㵕䱛

Slide 14

Slide 14 text

Motivation
 㵕䱛 - Rust - Like C: compiled, statically typed, very fast - Unlike C: enjoyable to use, guarantees safety - “If it compiles, it doesn’t crash” - Same guarantee as Ruby, but without GC

Slide 15

Slide 15 text

Motivation
 㵕䱛 - Zero-cost abstractions™ - In Ruby: tension between abstractions and performance - Hash.keys.each vs Hash.each_key - In Rust: no such tradeoff - Compiler is magic

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

Motivation
 㵕䱛 fast_blank in Rust

Slide 19

Slide 19 text

Motivation
 㵕䱛

Slide 20

Slide 20 text

Motivation
 㵕䱛 fast_blank in Helix

Slide 21

Slide 21 text

Motivation
 㵕䱛 - The vision - Keep writing the Ruby you love… - …without the fear of eventually hitting a wall - Start with Ruby - Move pieces to Helix when appropriate

Slide 22

Slide 22 text

Demo 䋚ᄍ

Slide 23

Slide 23 text

$ http://chancancode.tv/helix ᝕承ਁ଒ %

Slide 24

Slide 24 text

& http://usehelix.com

Slide 25

Slide 25 text

How it works 䋚ᤰ΄托奞

Slide 26

Slide 26 text

Challenges 抓氂;䌏ᒽ

Slide 27

Slide 27 text

Challenge #1 Ease of use ֵ͚Κͯͫ

Slide 28

Slide 28 text

VALUE greeter_hello(VALUE self, VALUE name) { return rb_sprintf("Hello, %"PRIsVALUE".", name); } void Init_my_extension() { VALUE c_Greeter = rb_define_class("Greeter", rb_cObject); rb_define_singleton_method(c_Greeter, "hello", greeter_hello, 1); } class Greeter def self.hello(name) "Hello, #{name}." end end Ease of use
 ֵ͚Κͯͫ

Slide 29

Slide 29 text

class Greeter def initialize(name) @name = name end def hello "Hello, #{@name}.” end end struct Greeter { VALUE name; }; void Greeter_mark(struct Greeter* data) { rb_gc_mark(data->name); } VALUE Greeter_alloc(VALUE self) { struct Greeter* greeter; return Data_Make_Struct(self, struct Greeter, Greeter_mark, RUBY_DEFAULT_FREE, greeter); } VALUE Greeter_initialize(VALUE self, VALUE name) { struct Greeter* greeter; Data_Get_Struct(self, struct Greeter, greeter); *greeter = name; return self; } VALUE greeter_hello(VALUE self) { return rb_sprintf("Hello, %"PRIsVALUE".", name); } void Init_my_extension() { VALUE c_Greeter = rb_define_class("Greeter", rb_cObject); rb_define_alloc_func(c_Greeter, Greeter_alloc); rb_define_method(c_Greeter, "initialize", Greeter_initialize, 1); rb_define_method(c_Greeter, "hello", greeter_hello, 0); } Ease of use
 ֵ͚Κͯͫ

Slide 30

Slide 30 text

class Point def initialize(x, y) @x = x @y = y end attr_reader :x, :y def +(other) Point.new(@x + other.x, @y + other.y) end def -(other) Point.new(@x - other.x, @y - other.y) end def distance_from(other) delta = self - other Math.sqrt(delta.x ** 2 + delta.y ** 2) end end ' Ease of use
 ֵ͚Κͯͫ

Slide 31

Slide 31 text

Domain-specific language ϖϮαЀࢴํ᥺承

Slide 32

Slide 32 text

class Greeter def self.hello(name) "Hello, #{name}." end end ruby! { class Greeter { def hello(name: String) -> String { format!("Hello, {}.", name) } } } Ease of use
 ֵ͚Κͯͫ

Slide 33

Slide 33 text

class Greeter def initialize(name) @name = name end def hello "Hello, #{@name}.” end end ruby! { class Greeter { struct { name: String } def initialize(helix, name: String) { Greeter { helix, name } } def hello(&self) -> String { format!("Hello, {}.", self.name) } } } Ease of use
 ֵ͚Κͯͫ

Slide 34

Slide 34 text

ruby! { class Point { struct { x: f64, y: f64 } def initialize(helix, x: f64, y: f64) { Point { helix, x, y } } def x(&self) -> f64 { self.x } def y(&self) -> f64 { self.y } #[ruby_name="+"] def add(&self, other: &Point) -> Point { Point::new(self.x + other.x, self.y + other.y) } #[ruby_name="-"] def subtract(&self, other: &Point) -> Point { Point::new(self.x - other.x, self.y - other.y) } def distance_from(&self, other: &Point) -> f64 { let Point { x, y, .. } = self.subtract(&other); x.hypot(y) } } } class Point def initialize(x, y) @x = x @y = y end attr_reader :x, :y def +(other) Point.new(@x + other.x, @y + other.y) end def -(other) Point.new(@x - other.x, @y - other.y) end def distance_from(other) delta = self - other Math.sqrt(delta.x ** 2 + delta.y ** 2) end end Ease of use
 ֵ͚Κͯͫ

Slide 35

Slide 35 text

But how? Ϳ͜Κ͹ͼҘ

Slide 36

Slide 36 text

But how?
 Ϳ͜Κ͹ͼҘ - Write our own parser? - But… we need to parse both languages at the same time - Also… parsing Rust can be quite difficult

Slide 37

Slide 37 text

But how?
 Ϳ͜Κ͹ͼҘ impl Running> for Left

where P: Running>> { type Output =

>>>::Output; } From “Rust's Type System is Turing-Complete”

Slide 38

Slide 38 text

But how?
 Ϳ͜Κ͹ͼҘ - Need something that understands Rust - Thankfully that already exists - Rust’s macro system

Slide 39

Slide 39 text

But how?
 Ϳ͜Κ͹ͼҘ - Macros in C: - Simple text substitution - Macro pre-processor ➡ parser…compiler - Macros in Rust: - Pattern matching - Tokenizer/parser* ➡ macro expansion ➡ …compiler

Slide 40

Slide 40 text

But how?
 Ϳ͜Κ͹ͼҘ struct Point { x: f64, y: f64 } impl Point { fn x(&self) -> f64 { self.x } fn y(&self) -> f64 { self.y } // ...other methods... }

Slide 41

Slide 41 text

But how?
 Ϳ͜Κ͹ͼҘ struct Point { x: f64, y: f64 } impl Point { fn x(&self) -> f64 { self.x } fn y(&self) -> f64 { self.y } // ...other methods... }

Slide 42

Slide 42 text

struct Point { x: f64, y: f64 } attr_reader!(Point, x: f64); attr_reader!(Point, y: f64); But how?
 Ϳ͜Κ͹ͼҘ

Slide 43

Slide 43 text

struct Point { x: f64, y: f64 } attr_reader!(Point, x: f64); attr_reader!(Point, y: f64); But how?
 Ϳ͜Κ͹ͼҘ macro_rules! attr_reader { ( $struct_name:ident, $field_name:ident : $field_type:ty ) => { impl $struct_name { fn $field_name(&self) -> $field_type { self.$field_name } } }; }

Slide 44

Slide 44 text

struct Point { x: f64, y: f64 } attr_reader!(Point, x: f64); attr_reader!(Point, y: f64); But how?
 Ϳ͜Κ͹ͼҘ macro_rules! attr_reader { ( $struct_name:ident, $field_name:ident : $field_type:ty ) => { impl $struct_name { fn $field_name(&self) -> $field_type { self.$field_name } } }; }

Slide 45

Slide 45 text

struct Point { x: f64, y: f64 } attr_reader!(Point, x: f64); attr_reader!(Point, y: f64); But how?
 Ϳ͜Κ͹ͼҘ macro_rules! attr_reader { ( $struct_name:ident, $field_name:ident : $field_type:ty ) => { impl $struct_name { fn $field_name(&self) -> $field_type { self.$field_name } } }; }

Slide 46

Slide 46 text

struct Point { x: f64, y: f64 } attr_reader!(Point, x: f64); attr_reader!(Point, y: f64); But how?
 Ϳ͜Κ͹ͼҘ macro_rules! attr_reader { ( $struct_name:ident, $field_name:ident : $field_type:ty ) => { impl $struct_name { fn $field_name(&self) -> $field_type { self.$field_name } } }; }

Slide 47

Slide 47 text

struct Point { x: f64, y: f64 } attr_reader!(Point, x: f64); attr_reader!(Point, y: f64); But how?
 Ϳ͜Κ͹ͼҘ macro_rules! attr_reader { ( $struct_name:ident, $field_name:ident : $field_type:ty ) => { impl $struct_name { fn $field_name(&self) -> $field_type { self.$field_name } } }; }

Slide 48

Slide 48 text

struct Point { x: f64, y: f64 } attr_reader!(Point, x: f64); attr_reader!(Point, y: f64); But how?
 Ϳ͜Κ͹ͼҘ macro_rules! attr_reader { ( $struct_name:ident, $field_name:ident : $field_type:ty ) => { impl $struct_name { fn $field_name(&self) -> $field_type { self.$field_name } } }; }

Slide 49

Slide 49 text

struct Point { x: f64, y: f64 } attr_reader!(Point, x: f64); attr_reader!(Point, y: f64); But how?
 Ϳ͜Κ͹ͼҘ macro_rules! attr_reader { ( $struct_name:ident, $field_name:ident : $field_type:ty ) => { impl $struct_name { fn $field_name(&self) -> $field_type { self.$field_name } } }; }

Slide 50

Slide 50 text

struct Point { x: f64, y: f64 } attr_reader!(Point, x: f64); attr_reader!(Point, y: f64); But how?
 Ϳ͜Κ͹ͼҘ

Slide 51

Slide 51 text

struct Point { x: f64, y: f64 } attr_reader!(Point, x: f64); attr_reader!(Point, y: f64); But how?
 Ϳ͜Κ͹ͼҘ

Slide 52

Slide 52 text

struct Point { x: f64, y: f64 } attr_reader!(Point, x: f64); attr_reader!(Point, y: f64); But how?
 Ϳ͜Κ͹ͼҘ

Slide 53

Slide 53 text

struct Point { x: f64, y: f64 } attr_reader!(Point, x: f64); attr_reader!(Point, y: f64); But how?
 Ϳ͜Κ͹ͼҘ

Slide 54

Slide 54 text

struct Point { x: f64, y: f64 } attr_reader!(Point, x: f64); attr_reader!(Point, y: f64); But how?
 Ϳ͜Κ͹ͼҘ

Slide 55

Slide 55 text

TMI ᥺͚ͯͤ

Slide 56

Slide 56 text

But how?
 Ϳ͜Κ͹ͼҘ - Rust’s macro system has some restrictions - Recursion limit - Hygiene - Each step must expand into valid Rust syntax

Slide 57

Slide 57 text

But how?
 Ϳ͜Κ͹ͼҘ

Slide 58

Slide 58 text

But how?
 Ϳ͜Κ͹ͼҘ

Slide 59

Slide 59 text

But how?
 Ϳ͜Κ͹ͼҘ

Slide 60

Slide 60 text

But how?
 Ϳ͜Κ͹ͼҘ

Slide 61

Slide 61 text

Pushdown automata ϤϐτϲύγЀ独ηЄϕϫϕЀ

Slide 62

Slide 62 text

But how?
 Ϳ͜Κ͹ͼҘ

Slide 63

Slide 63 text

But how?
 Ϳ͜Κ͹ͼҘ

Slide 64

Slide 64 text

But how?
 Ϳ͜Κ͹ͼҘ

Slide 65

Slide 65 text

But how?
 Ϳ͜Κ͹ͼҘ

Slide 66

Slide 66 text

But how?
 Ϳ͜Κ͹ͼҘ

Slide 67

Slide 67 text

But how?
 Ϳ͜Κ͹ͼҘ

Slide 68

Slide 68 text

But how?
 Ϳ͜Κ͹ͼҘ

Slide 69

Slide 69 text

But how?
 Ϳ͜Κ͹ͼҘ

Slide 70

Slide 70 text

But how?
 Ϳ͜Κ͹ͼҘ

Slide 71

Slide 71 text

But how?
 Ϳ͜Κ͹ͼҘ

Slide 72

Slide 72 text

Slide 73

Slide 73 text

Challenge #2 Type safety ࣳਞق௔

Slide 74

Slide 74 text

Type safety
 ࣳਞق௔ Let’s look at Ruby’s C API

Slide 75

Slide 75 text

Type safety
 ࣳਞق௔ // create a new String VALUE rb_str_new(const char*, long);
 // define a new class/module
 VALUE rb_define_class(const char*, VALUE); VALUE rb_define_module(const char*); VALUE rb_define_class_under(VALUE, const char*, VALUE); VALUE rb_define_module_under(VALUE, const char*);

Slide 76

Slide 76 text

Type safety
 ࣳਞق௔ ) VALUEs everywhere

Slide 77

Slide 77 text

Type safety
 ࣳਞق௔ typedef uintptr_t VALUE;

Slide 78

Slide 78 text

Type safety
 ࣳਞق௔ typedef void* VALUE;

Slide 79

Slide 79 text

Type safety
 ࣳਞق௔ * VALUEs everywhere

Slide 80

Slide 80 text

Type safety
 ࣳਞق௔ + Safety

Slide 81

Slide 81 text

Type safety
 ࣳਞق௔ - Rust: - Memory safety % - Advanced type system features

Slide 82

Slide 82 text

Type safety
 ࣳਞق௔ - Type inference - Generics - Lifetime - Zero-cost abstractions™

Slide 83

Slide 83 text

Type safety
 ࣳਞق௔ Zero-cost abstractions™

Slide 84

Slide 84 text

Type safety
 ࣳਞق௔

Slide 85

Slide 85 text

Type safety
 ࣳਞق௔ type VALUE = *mut void;

Slide 86

Slide 86 text

Type safety
 ࣳਞق௔ struct VALUE(*mut void);

Slide 87

Slide 87 text

Type safety
 ࣳਞق௔ struct VALUE { inner: *mut void }

Slide 88

Slide 88 text

Type safety
 ࣳਞق௔ struct VALUE { inner: *mut void } size_of::() == size_of::<*mut void>()

Slide 89

Slide 89 text

struct CheckedValue { inner: VALUE, target_type: PhantomData } Type safety
 ࣳਞق௔ size_of::>() == 0

Slide 90

Slide 90 text

struct CheckedValue { inner: VALUE, target_type: PhantomData } Type safety
 ࣳਞق௔ size_of::>() == size_of::() + 0 == size_of::<*mut void>()

Slide 91

Slide 91 text

Coercion ࣳ䄜䟵

Slide 92

Slide 92 text

Type safety
 ࣳਞق௔ ruby! { class Greeter { def hello(name: String) -> String { format!("Hello, {}.", name) } } }

Slide 93

Slide 93 text

# From Ruby >> Greeter.hello(“Godfrey”) => “Hello, Godfrey.” >> Greeter.hello(123456) TypeError: Expected a UTF-8 String, got 123456 Type safety
 ࣳਞق௔

Slide 94

Slide 94 text

But how? Ϳ͜Κ͹ͼҘ

Slide 95

Slide 95 text

extern "C" fn Greeter_hello(unchecked_name: VALUE) -> VALUE { let maybe_name = UncheckedValue::::to_checked(unchecked_name); let name = match maybe_name { Ok(checked_name) => { checked_name.to_rust() }, Err(message) => { unsafe { rb_raise(message) } } }; let result: String = format!(“Hello, {}.", name); result.to_ruby() } But how?
 Ϳ͜Κ͹ͼҘ

Slide 96

Slide 96 text

extern "C" fn Greeter_hello(unchecked_name: VALUE) -> VALUE { let maybe_name = UncheckedValue::::to_checked(unchecked_name); let name = match maybe_name { Ok(checked_name) => { checked_name.to_rust() }, Err(message) => { unsafe { rb_raise(message) } } }; let result: String = format!(“Hello, {}.", name); result.to_ruby() } But how?
 Ϳ͜Κ͹ͼҘ raw VALUE (but not just any void pointer!)

Slide 97

Slide 97 text

extern "C" fn Greeter_hello(unchecked_name: VALUE) -> VALUE { let maybe_name = UncheckedValue::::to_checked(unchecked_name); let name = match maybe_name { Ok(checked_name) => { checked_name.to_rust() }, Err(message) => { unsafe { rb_raise(message) } } }; let result: String = format!(“Hello, {}.", name); result.to_ruby() } But how?
 Ϳ͜Κ͹ͼҘ Result, Error>

Slide 98

Slide 98 text

impl UncheckedValue for VALUE { fn to_checked(self) -> CheckResult { if unsafe { sys::RB_TYPE_P(self, sys::T_STRING) } { Ok(unsafe { CheckedValue::::new(self) }) } else { Err(::invalid(self, "a UTF-8 String")) } } } But how?
 Ϳ͜Κ͹ͼҘ

Slide 99

Slide 99 text

extern "C" fn Greeter_hello(unchecked_name: VALUE) -> VALUE { let maybe_name = UncheckedValue::::to_checked(unchecked_name); let name = match maybe_name { Ok(checked_name) => { checked_name.to_rust() }, Err(message) => { unsafe { rb_raise(message) } } }; let result: String = format!(“Hello, {}.", name); result.to_ruby() } But how?
 Ϳ͜Κ͹ͼҘ CheckedValue Error

Slide 100

Slide 100 text

extern "C" fn Greeter_hello(unchecked_name: VALUE) -> VALUE { let maybe_name = UncheckedValue::::to_checked(unchecked_name); let name = match maybe_name { Ok(checked_name) => { checked_name.to_rust() }, Err(message) => { unsafe { rb_raise(message) } } }; let result: String = format!(“Hello, {}.", name); result.to_ruby() } But how?
 Ϳ͜Κ͹ͼҘ String

Slide 101

Slide 101 text

impl ToRust for CheckedValue { fn to_rust(self) -> String { let size = unsafe { RSTRING_LEN(self.inner) as usize }; let ptr = unsafe { RSTRING_PTR(self.inner) as *const u8 }; let slice = unsafe { slice::from_raw_parts(ptr, size) }; unsafe { std::str::from_utf8_unchecked(slice) }.to_string() } } But how?
 Ϳ͜Κ͹ͼҘ

Slide 102

Slide 102 text

impl ToRust for CheckedValue { fn to_rust(self) -> String { let size = unsafe { RSTRING_LEN(self.inner) as usize }; let ptr = unsafe { RSTRING_PTR(self.inner) as *const u8 }; let slice = unsafe { slice::from_raw_parts(ptr, size) }; unsafe { std::str::from_utf8_unchecked(slice) }.to_string() } } But how?
 Ϳ͜Κ͹ͼҘ CheckedValue == VALUE == *mut void

Slide 103

Slide 103 text

extern "C" fn Greeter_hello(unchecked_name: VALUE) -> VALUE { let maybe_name = UncheckedValue::::to_checked(unchecked_name); let name = match maybe_name { Ok(checked_name) => { checked_name.to_rust() }, Err(message) => { unsafe { rb_raise(message) } } }; let result: String = format!(“Hello, {}.", name); result.to_ruby() } But how?
 Ϳ͜Κ͹ͼҘ String

Slide 104

Slide 104 text

extern "C" fn Greeter_hello(unchecked_name: VALUE) -> VALUE { let maybe_name = UncheckedValue::::to_checked(unchecked_name); let name = match maybe_name { Ok(checked_name) => { checked_name.to_rust() }, Err(message) => { unsafe { rb_raise(message) } } }; let result: String = format!(“Hello, {}.", name); result.to_ruby() } But how?
 Ϳ͜Κ͹ͼҘ String

Slide 105

Slide 105 text

But how?
 Ϳ͜Κ͹ͼҘ

Slide 106

Slide 106 text

extern "C" fn some_method(arg1_raw: VALUE, arg2_raw: VALUE, …) -> VALUE { let arg1_result = UncheckedValue::::to_checked(arg1_raw); let arg2_result = UncheckedValue::::to_checked(arg2_raw); // … let arg1 = match arg1_result { Ok(checked) => { checked.to_rust() }, // Err(m) => … }; let arg2 = match arg2_result { Ok(checked) => { checked.to_rust() }, // Err(m) => … }; // … let result: T_return = { /* user code */ }; result.to_ruby() } But how?
 Ϳ͜Κ͹ͼҘ

Slide 107

Slide 107 text

Challenge #3 Distribution ᯈ૲

Slide 108

Slide 108 text

Distribution
 ᯈ૲ - Requires Rust compiler on servers - Potentially not a big deal - Not always possible, leads to lower adoption - Longer bundle install times

Slide 109

Slide 109 text

Distribution
 ᯈ૲ - libv8 gem faced similar problems - Takes a (really) long time to compile v8 - Uses Google's own build system (gyp) - Requires Python 2.7

Slide 110

Slide 110 text

Distribution
 ᯈ૲ Precompiles binaries for libv8 gem

Slide 111

Slide 111 text

Distribution
 ᯈ૲ - Another example: skylight gem - Written in Rust - Supports Linux + Mac OS X - Custom VM image to build & publish binary - gem install skylight Just Works™

Slide 112

Slide 112 text

But how? Ϳ͜Κ͹ͼҘ

Slide 113

Slide 113 text

But how?
 Ϳ͜Κ͹ͼҘ - Cross-compilation? - Rust: good support out-of-the-box - Ruby: not so much

Slide 114

Slide 114 text

But how?
 Ϳ͜Κ͹ͼҘ - Use CI to build natively on each platform - 64-bit Linux (Travis CI / Circle CI) - 64-bit Mac OS X (Travis CI / Circle CI) - 32/64-bit Windows (Appveyor) - Script to publish directly to RubyGems from CI

Slide 115

Slide 115 text

But how?
 Ϳ͜Κ͹ͼҘ Gem::Specification.new do |s| s.name = 'text_transform' s.version = '0.1.0' s.authors = ['Godfrey Chan', 'Terence Lee'] s.summary = "Transform text using the power of helix/rust" s.platform = Gem::Platform.local s.require_path = 'lib'
 s.add_dependency 'helix_runtime', '~> 0.6.4' s.add_development_dependency 'rspec', '~> 3.6' end

Slide 116

Slide 116 text

But how?
 Ϳ͜Κ͹ͼҘ Gem::Specification.new do |s| s.name = 'text_transform' s.version = '0.1.0' s.authors = ['Godfrey Chan', 'Terence Lee'] s.summary = "Transform text using the power of helix/rust" s.platform = Gem::Platform.local s.require_path = 'lib'
 s.add_dependency 'helix_runtime', '~> 0.6.4' s.add_development_dependency 'rspec', '~> 3.6' end

Slide 117

Slide 117 text

But how?
 Ϳ͜Κ͹ͼҘ system "gem build text_transform.gemspec" if GIT_TAG && GIT_TAG.match(/^v[0-9.]+/) credentials_file = "~/.gem/credentials" FileUtils.mkdir_p gem_config_dir File.open(credentials_file, 'w') do |f| f.puts YAML.dump({ rubygems_api_key: ENV['RUBYGEMS_AUTH_TOKEN'] }) end File.chmod 0600, credentials_file system "gem push text_transform-*.gem” end

Slide 118

Slide 118 text

But how?
 Ϳ͜Κ͹ͼҘ system "gem build text_transform.gemspec" if GIT_TAG && GIT_TAG.match(/^v[0-9.]+/) credentials_file = "~/.gem/credentials" FileUtils.mkdir_p gem_config_dir File.open(credentials_file, 'w') do |f| f.puts YAML.dump({ rubygems_api_key: ENV['RUBYGEMS_AUTH_TOKEN'] }) end File.chmod 0600, credentials_file system "gem push text_transform-*.gem” end

Slide 119

Slide 119 text

- What about 32-bit Linux? - Not supported by public Travis/Circle CI offering - Option is to use 32-bit Linux Docker Image - Also not officially supported by Docker - But how?
 Ϳ͜Κ͹ͼҘ

Slide 120

Slide 120 text

Roadmap ϺЄϖϫϐϤ

Slide 121

Slide 121 text

Roadmap
 ϺЄϖϫϐϤ - Good use cases today: - CPU-bound - Simple inputs - Avoid chatty APIs

Slide 122

Slide 122 text

Roadmap
 ϺЄϖϫϐϤ - Good use cases today: - Use Rust libraries - Leverage Rust web browser tech - Mailer, Background job, Action Cable

Slide 123

Slide 123 text

Roadmap
 ϺЄϖϫϐϤ - Greenfield project - Drop-in replacement - Reopen class - Ship to production - Binary distribution - Non-traditional use-cases - Performance parity with C - Miscellaneous features and QoL improvements

Slide 124

Slide 124 text

No content

Slide 125

Slide 125 text

& http://usehelix.com

Slide 126

Slide 126 text

How to help 揙ሠͯΡොဩ

Slide 127

Slide 127 text

How to help
 揙ሠͯΡොဩ - Please try it! - Hack on Helix together during Ruby Kaigi? - Need help with… - Debugging Windows, Linux 32-bit, linker questions etc - Cross-compilation - Documentation - Funding?

Slide 128

Slide 128 text

Thank you ͘Π͢;͚ͪͬ͜Δͭ͵