Slide 1

Slide 1 text

Rust + Ruby = !

Slide 2

Slide 2 text

First, a little about Rust

Slide 3

Slide 3 text

Memory safety

Slide 4

Slide 4 text

fn main() { let mut x = vec!["Hello", "world"]; }

Slide 5

Slide 5 text

fn main() { let mut x = vec!["Hello", "world"]; let y = &x[0]; }

Slide 6

Slide 6 text

fn main() { let mut x = vec!["Hello", "world"]; let y = &x[0]; x.push("foo"); }

Slide 7

Slide 7 text

fn main() { let mut x = vec!["Hello", "world"]; let y = &x[0]; x.push("foo"); } error: cannot borrow `x` as mutable because it is also borrowed as immutable x.push("foo"); ^ note: previous borrow of `x` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `x` until the borrow ends let y = &x[0]; ^ note: previous borrow ends here fn main() { } ^

Slide 8

Slide 8 text

irb(main):001:0> a = %w( no you're stupid ) # => ["no", "you're", "stupid"] irb(main):002:0> x = a[0] # => "no" irb(main):003:0> x.upcase! # => "NO" irb(main):004:0> a # => ["NO", "you're", "stupid"]

Slide 9

Slide 9 text

Structs

Slide 10

Slide 10 text

pub struct Usage { calls: HashMap, total: u32 }

Slide 11

Slide 11 text

impl Usage { fn new() -> Usage { Usage { calls: HashMap::new(), total: 0 } } fn record(&mut self, method_name: String) { *self.calls.entry(method_name).or_insert(0) += 1; self.total += 1; } }

Slide 12

Slide 12 text

Let's talk about Ruby for a sec

Slide 13

Slide 13 text

@calls = Hash.new(0) @totals = 0 def record(event, file, line, id, classname) if %w( call c-call ).include?(event) @calls["#{classname}##{id}"] += 1 @totals += 1 end end set_trace_func proc { |event, file, line, id, binding, classname| record(event, file, line, id, classname) }

Slide 14

Slide 14 text

Can we port that bottleneck to Rust?

Slide 15

Slide 15 text

pub struct Usage { calls: HashMap, total: u32 } impl Usage { fn new() -> Usage { Usage { calls: HashMap::new(), total: 0 } } fn record(&mut self, method_name: String) { *self.calls.entry(method_name).or_insert(0) += 1; self.total += 1; } }

Slide 16

Slide 16 text

pub fn record(usage: &mut Usage, event: &str, file: &str, line: u32, id: &str, class_name: &str) { if event == "call" || event == "c-call" { usage.record(class_name.to_string() + "#" + method_name) } }

Slide 17

Slide 17 text

#[no_mangle] pub extern "C" fn record(usage: &mut Usage, event: *const c_char, file: *const c_char, line: u32, id: *const c_char, class_name: *const c_char) { let event = unsafe { CStr::from_ptr(event) }; let event = str::from_utf8(event.to_bytes()).unwrap(); if event == "call" || event == "c-call" { let method_name = unsafe { CStr::from_ptr(id) }; let method_name = str::from_utf8(method_name.to_bytes()).unwrap(); let class_name = unsafe { CStr::from_ptr(class_name) }; let class_name = str::from_utf8(class_name.to_bytes()).unwrap(); usage.record(class_name.to_string() + "#" + method_name) } }

Slide 18

Slide 18 text

#[no_mangle] pub extern "C" fn new_usage() -> Box { Box::new(Usage::new()) }

Slide 19

Slide 19 text

module Rust extend FFI::Library ffi_lib 'target/release/librusttrace.dylib' attach_function :new_usage, [], :pointer attach_function :record, [:pointer, :string, :string, :int, :string, :string], :void end

Slide 20

Slide 20 text

@usage = Rust.new_usage set_trace_func proc { |event, file, line, id, binding, classname| Rust.record(@usage, event, file, line, id.to_s, classname.to_s) }

Slide 21

Slide 21 text

What about getting values back from Rust functions?

Slide 22

Slide 22 text

pub struct CallCount { count: u32, method_name: String } impl CallCount { fn new(method: &str, count: &u32) -> CallCount { CallCount { method_name: method.to_string(), count: *count } } } fn report(usage: &Usage) -> Vec { let mut counts: Vec = usage.calls .iter() .map(|(method, count)| CallCount::new(method, count) ) .collect(); counts.sort_by(|a, b| a.count.cmp(&b.count).reverse()); counts }

Slide 23

Slide 23 text

#[repr(C)] pub struct CallCount { count: u32, method_name: *const c_char } impl CallCount { fn new(method: &str, count: &u32) -> CallCount { let method_name = self.method_name.clone(); let method_name = CString::new(method_name).unwrap(); CallCount { method_name: method.into_ptr(), count: *count } } } fn report(usage: &Usage) -> Vec { let mut counts: Vec = usage.calls .iter() .map(|(method, count)| CallCount::new(method, count) ) .collect(); counts.sort_by(|a, b| a.count.cmp(&b.count).reverse()); counts }

Slide 24

Slide 24 text

pub struct Report { length: usize, call_counts: *const CallCount } fn report(usage: &Usage) -> Report { let mut counts: Vec = usage.calls .iter() .map(|(method, count)| CallCount::new(method, count) ) .collect(); counts.sort_by(|a, b| a.count.cmp(&b.count).reverse()); Report { length: counts.len(), call_counts: counts.as_ptr() } }

Slide 25

Slide 25 text

Meanwhile, in Ruby

Slide 26

Slide 26 text

class CallCount < FFI::Struct layout :count, :uint, :method_name, :string def method_name self[:method_name] end def count self[:count] end end class Report < FFI::Struct include Enumerable layout :length, :uint, :call_counts, :pointer def each self[:length].times do |i| yield CallCount.new(self[:call_counts] + (i * CallCount.size)) end end end

Slide 27

Slide 27 text

module Rust extend FFI::Library ffi_lib 'target/release/librusttrace.dylib' attach_function :new_usage, [], :pointer attach_function :record, [:pointer, :string, :string, :int, :string, :string], :void attach_function :report, [:pointer], Report.by_value end

Slide 28

Slide 28 text

Rust.report(@usage)

Slide 29

Slide 29 text

module Rusttrace class Usage module Rust extend FFI::Library ffi_lib 'target/release/librusttrace.dylib' attach_function :new_usage, [], :pointer attach_function :record, [:pointer, :string, :string, :int, :string, :string], :void attach_function :report, [:pointer], Report.by_value end def initialize @usage = Rust.new_usage end def attach set_trace_func proc { |event, file, line, id, binding, classname| Rust.record(@usage, event, file, line, id.to_s, classname.to_s) } end def report Rust.report(@usage) end end end

Slide 30

Slide 30 text

rusttrace.herokuapp.com/ report