Turbo Rails with Rust

Turbo Rails with Rust

Ruby is not the fastest language in the world, there is no doubt about it. This doesn't turn out to matter all that much – Ruby and its ecosystem has so much more to offer, making it a worthwhile tradeoff a lot of the times.

However, you might occasionally encounter workloads that are simply not suitable for Ruby. This is especially true for frameworks like Rails, where the overhead wants to be as little as possible.

In this talk, we will explore building a native Ruby extension with Rust to speed up parts of Rails. What does Rust have to offer here over plain-old C? Let's find out!

22bb3e56828870ee9a0dd93aeadbe04a?s=128

Godfrey Chan

May 06, 2016
Tweet

Transcript

  1. None
  2. WELCOME TO KANSAS

  3. WIZARD OF orz

  4. None
  5. None
  6. KANSAS MISSOURI

  7. KANSAS MISSOURI KANSAS CITY

  8. KANSAS MISSOURI KANSAS CITY

  9. @chancancode GODFREY CHAN

  10. None
  11. None
  12. None
  13. ! " # $ %

  14. RAILS 5

  15. PHP

  16. JavaScript

  17. gem install javascript

  18. require "javascript" puts "This is totally Ruby" javascript { console.log("ZOMG

    JavaScript"); } puts "This is Ruby again"
  19. require "javascript" puts "This is totally Ruby" javascript { console.log("ZOMG

    JavaScript"); } puts "This is Ruby again"
  20. require "javascript" puts "This is totally Ruby" javascript { console.log("ZOMG

    JavaScript"); } puts "This is Ruby again"
  21. require "javascript" javascript { var plusOne = function(x) { console.log(x

    + 1); }; plusOne(3); # => 4 }
  22. Everyone's favorite JavaScript feature?

  23. Callbacks

  24. Everyone's favorite Rails feature?

  25. Callbacks

  26. require "javascript" class Post < ActiveRecord::Base before_create &javascript { function()

    { this.slug = this.title.parameterize(); } } end
  27. require "javascript" class Post < ActiveRecord::Base before_create &javascript { function()

    { this.slug = this.title.parameterize(); } } end
  28. None
  29. None
  30. Subscribe Today! http://bit.ly/rails-weekly

  31. None
  32. SIGN UP TODAY! https://skylight.io

  33. SIGN UP TODAY! https://skylight.io/r/r1Z8ogwwN05Z

  34. RUBY

  35. C

  36. Best of both worlds* NATIVE EXTENSIONS

  37. gem install json

  38. gem install json JSON::Pure Pure Ruby

  39. gem install json JSON::Ext Native Extension JSON::Pure Pure Ruby

  40. Best of both worlds* NATIVE EXTENSIONS

  41. Best of both worlds* NATIVE EXTENSIONS

  42. Best of both worlds* NATIVE EXTENSIONS for the end-user

  43. "Here’s a general value for Rails development: We will jump

    through hoops on the implementation to make the user-facing API nicer." David Heinemeier Hansson
  44. "Here’s a general value for Rails development: We will jump

    through hoops on the implementation to make the user-facing API nicer." David Heinemeier Hansson
  45. "Here’s a general value for Rails development: We will jump

    through hoops on the implementation to make the user-facing API nicer." David Heinemeier Hansson developer experience
  46. @SamSaffron

  47. String#blank?

  48. class String def blank? /\A[[:space:]]*\z/ === self end end

  49. ! SamSaffron/fast_blank

  50. UP TO 20X FASTER*

  51. & ME

  52. & ME knows just enough C to be dangerous

  53. & ME knows just enough C to be dangerous ptr

  54. & ME knows just enough C to be dangerous *ptr

  55. & ME knows just enough C to be dangerous **********ptr

  56. & ME knows just enough C to be dangerous &&&&&&&&&&&&&&&&&&&&ptr

  57. ' SEGV

  58. None
  59. FEATHERWEIGHT AGENT

  60. RUST

  61. Rust is a systems programming language that runs blazingly fast,

    prevents segfaults, and guarantees thread safety.
  62. HACK WITHOUT FEAR

  63. This is Rust. Rust has a compiler. Rust's compiler finds

    errors at compile- time. Rust's compiler guarantees the compiled program cannot crash at runtime. Be like Rust.
  64. without garbage collection MEMORY SAFETY

  65. without data races CONCURRENCY

  66. ZERO-COST ABSTRACTIONS

  67. None
  68. BACK TO FAST_BLANK

  69. FAST_BLANK IN C

  70. static VALUE rb_str_blank(VALUE str) { rb_encoding *enc; char *s, *e;

    enc = STR_ENC_GET(str); s = RSTRING_PTR(str); if (!s || RSTRING_LEN(str) == 0) return Qtrue; e = RSTRING_END(str); while (s < e) { int n; unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc); switch (cc) { case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0x20: case 0x85: case 0xa0: case 0x1680: case 0x2000: case 0x2001: case 0x2002: case 0x2003: case 0x2004: case 0x2005: case 0x2006: case 0x2007: case 0x2008: case 0x2009: case 0x200a: case 0x2028: case 0x2029: case 0x202f: case 0x205f: case 0x3000: /* found */ break; default: return Qfalse; } s += n; } return Qtrue; }
  71. static VALUE rb_str_blank(VALUE str) { rb_encoding *enc; char *s, *e;

    enc = STR_ENC_GET(str); s = RSTRING_PTR(str); if (!s || RSTRING_LEN(str) == 0) return Qtrue; e = RSTRING_END(str); while (s < e) { int n; unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc); switch (cc) { case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0x20: case 0x85: case 0xa0: case 0x1680: case 0x2000: case 0x2001: case 0x2002: case 0x2003: case 0x2004: case 0x2005: case 0x2006: case 0x2007: case 0x2008: case 0x2009: case 0x200a: case 0x2028: case 0x2029: case 0x202f: case 0x205f: case 0x3000: /* found */ break; default: return Qfalse; } s += n; } return Qtrue; } static VALUE rb_str_blank(VALUE str) { }
  72. static VALUE rb_str_blank(VALUE str) { rb_encoding *enc; char *s, *e;

    enc = STR_ENC_GET(str); s = RSTRING_PTR(str); if (!s || RSTRING_LEN(str) == 0) return Qtrue; e = RSTRING_END(str); while (s < e) { int n; unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc); switch (cc) { case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0x20: case 0x85: case 0xa0: case 0x1680: case 0x2000: case 0x2001: case 0x2002: case 0x2003: case 0x2004: case 0x2005: case 0x2006: case 0x2007: case 0x2008: case 0x2009: case 0x200a: case 0x2028: case 0x2029: case 0x202f: case 0x205f: case 0x3000: /* found */ break; default: return Qfalse; } s += n; } return Qtrue; } static VALUE rb_str_blank(VALUE str) { // …boilerplate… }
  73. static VALUE rb_str_blank(VALUE str) { rb_encoding *enc; char *s, *e;

    enc = STR_ENC_GET(str); s = RSTRING_PTR(str); if (!s || RSTRING_LEN(str) == 0) return Qtrue; e = RSTRING_END(str); while (s < e) { int n; unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc); switch (cc) { case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0x20: case 0x85: case 0xa0: case 0x1680: case 0x2000: case 0x2001: case 0x2002: case 0x2003: case 0x2004: case 0x2005: case 0x2006: case 0x2007: case 0x2008: case 0x2009: case 0x200a: case 0x2028: case 0x2029: case 0x202f: case 0x205f: case 0x3000: /* found */ break; default: return Qfalse; } s += n; } return Qtrue; } static VALUE rb_str_blank(VALUE str) { // …boilerplate… if (!s || RSTRING_LEN(str) == 0) return Qtrue; }
  74. static VALUE rb_str_blank(VALUE str) { rb_encoding *enc; char *s, *e;

    enc = STR_ENC_GET(str); s = RSTRING_PTR(str); if (!s || RSTRING_LEN(str) == 0) return Qtrue; e = RSTRING_END(str); while (s < e) { int n; unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc); switch (cc) { case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0x20: case 0x85: case 0xa0: case 0x1680: case 0x2000: case 0x2001: case 0x2002: case 0x2003: case 0x2004: case 0x2005: case 0x2006: case 0x2007: case 0x2008: case 0x2009: case 0x200a: case 0x2028: case 0x2029: case 0x202f: case 0x205f: case 0x3000: /* found */ break; default: return Qfalse; } s += n; } return Qtrue; } static VALUE rb_str_blank(VALUE str) { // …boilerplate… if (!s || RSTRING_LEN(str) == 0) return Qtrue; while(s < RSTRING_END(str)) { s++; } }
  75. static VALUE rb_str_blank(VALUE str) { rb_encoding *enc; char *s, *e;

    enc = STR_ENC_GET(str); s = RSTRING_PTR(str); if (!s || RSTRING_LEN(str) == 0) return Qtrue; e = RSTRING_END(str); while (s < e) { int n; unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc); switch (cc) { case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0x20: case 0x85: case 0xa0: case 0x1680: case 0x2000: case 0x2001: case 0x2002: case 0x2003: case 0x2004: case 0x2005: case 0x2006: case 0x2007: case 0x2008: case 0x2009: case 0x200a: case 0x2028: case 0x2029: case 0x202f: case 0x205f: case 0x3000: /* found */ break; default: return Qfalse; } s += n; } return Qtrue; } static VALUE rb_str_blank(VALUE str) { // …boilerplate… if (!s || RSTRING_LEN(str) == 0) return Qtrue; while(s < RSTRING_END(str)) { // …boilerplate… s++; } }
  76. static VALUE rb_str_blank(VALUE str) { rb_encoding *enc; char *s, *e;

    enc = STR_ENC_GET(str); s = RSTRING_PTR(str); if (!s || RSTRING_LEN(str) == 0) return Qtrue; e = RSTRING_END(str); while (s < e) { int n; unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc); switch (cc) { case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0x20: case 0x85: case 0xa0: case 0x1680: case 0x2000: case 0x2001: case 0x2002: case 0x2003: case 0x2004: case 0x2005: case 0x2006: case 0x2007: case 0x2008: case 0x2009: case 0x200a: case 0x2028: case 0x2029: case 0x202f: case 0x205f: case 0x3000: /* found */ break; default: return Qfalse; } s += n; } return Qtrue; } static VALUE rb_str_blank(VALUE str) { // …boilerplate… if (!s || RSTRING_LEN(str) == 0) return Qtrue; while(s < RSTRING_END(str)) { // …boilerplate… switch (s) { } s++; } }
  77. static VALUE rb_str_blank(VALUE str) { rb_encoding *enc; char *s, *e;

    enc = STR_ENC_GET(str); s = RSTRING_PTR(str); if (!s || RSTRING_LEN(str) == 0) return Qtrue; e = RSTRING_END(str); while (s < e) { int n; unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc); switch (cc) { case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0x20: case 0x85: case 0xa0: case 0x1680: case 0x2000: case 0x2001: case 0x2002: case 0x2003: case 0x2004: case 0x2005: case 0x2006: case 0x2007: case 0x2008: case 0x2009: case 0x200a: case 0x2028: case 0x2029: case 0x202f: case 0x205f: case 0x3000: /* found */ break; default: return Qfalse; } s += n; } return Qtrue; } static VALUE rb_str_blank(VALUE str) { // …boilerplate… if (!s || RSTRING_LEN(str) == 0) return Qtrue; while(s < RSTRING_END(str)) { // …boilerplate… switch (cc) { case 9: case 0xa: … case 0x205f: case 0x3000: // whitespace break; } s++; } }
  78. static VALUE rb_str_blank(VALUE str) { rb_encoding *enc; char *s, *e;

    enc = STR_ENC_GET(str); s = RSTRING_PTR(str); if (!s || RSTRING_LEN(str) == 0) return Qtrue; e = RSTRING_END(str); while (s < e) { int n; unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc); switch (cc) { case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0x20: case 0x85: case 0xa0: case 0x1680: case 0x2000: case 0x2001: case 0x2002: case 0x2003: case 0x2004: case 0x2005: case 0x2006: case 0x2007: case 0x2008: case 0x2009: case 0x200a: case 0x2028: case 0x2029: case 0x202f: case 0x205f: case 0x3000: /* found */ break; default: return Qfalse; } s += n; } return Qtrue; } static VALUE rb_str_blank(VALUE str) { // …boilerplate… if (!s || RSTRING_LEN(str) == 0) return Qtrue; while(s < RSTRING_END(str)) { // …boilerplate… switch (cc) { case 9: case 0xa: … case 0x205f: case 0x3000: // whitespace break; default: return Qfalse; } s++; } }
  79. static VALUE rb_str_blank(VALUE str) { rb_encoding *enc; char *s, *e;

    enc = STR_ENC_GET(str); s = RSTRING_PTR(str); if (!s || RSTRING_LEN(str) == 0) return Qtrue; e = RSTRING_END(str); while (s < e) { int n; unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc); switch (cc) { case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0x20: case 0x85: case 0xa0: case 0x1680: case 0x2000: case 0x2001: case 0x2002: case 0x2003: case 0x2004: case 0x2005: case 0x2006: case 0x2007: case 0x2008: case 0x2009: case 0x200a: case 0x2028: case 0x2029: case 0x202f: case 0x205f: case 0x3000: /* found */ break; default: return Qfalse; } s += n; } return Qtrue; } static VALUE rb_str_blank(VALUE str) { // …boilerplate… if (!s || RSTRING_LEN(str) == 0) return Qtrue; while(s < RSTRING_END(str)) { // …boilerplate… switch (cc) { case 9: case 0xa: … case 0x205f: case 0x3000: // whitespace break; default: return Qfalse; } s++; } return Qtrue; }
  80. FAST_BLANK IN RUST

  81. pub extern "C" fn fast_blank(buf: Buf) -> bool { buf.as_slice().chars().all(|c|

    c.is_whitespace()) }
  82. pub extern "C" fn fast_blank(buf: Buf) -> bool { buf.as_slice().chars().all(|c|

    c.is_whitespace()) }
  83. pub extern "C" fn fast_blank(buf: Buf) -> bool { buf.as_slice().chars().all(|c|

    c.is_whitespace()) }
  84. pub extern "C" fn fast_blank(buf: Buf) -> bool { buf.as_slice().chars().all(|c|

    c.is_whitespace()) }
  85. pub extern "C" fn fast_blank(buf: Buf) -> bool { buf.as_slice().chars().all(|c|

    c.is_whitespace()) }
  86. pub extern "C" fn fast_blank(buf: Buf) -> bool { buf.as_slice().chars().all(|c|

    c.is_whitespace()) }
  87. pub extern "C" fn fast_blank(buf: Buf) -> bool { buf.as_slice().chars().all(|c|

    c.is_whitespace()) }
  88. pub extern "C" fn fast_blank(buf: Buf) -> bool { buf.as_slice().chars().all(|c|

    c.is_whitespace()) }
  89. =========== Test String Length: 6 =========== Rust 11.043M (± 3.5%)

    i/s - 54.744M C 10.583M (± 8.5%) i/s - 52.464M Ruby 964.469k (±27.6%) i/s - 4.390M
  90. =========== Test String Length: 6 =========== Rust 11.043M (± 3.5%)

    i/s - 54.744M C 10.583M (± 8.5%) i/s - 52.464M Ruby 964.469k (±27.6%) i/s - 4.390M
  91. =========== Test String Length: 6 =========== Rust 11.043M (± 3.5%)

    i/s - 54.744M C 10.583M (± 8.5%) i/s - 52.464M Ruby 964.469k (±27.6%) i/s - 4.390M
  92. pub extern "C" fn fast_blank(buf: Buf) -> bool { buf.as_slice().chars().all(|c|

    c.is_whitespace()) }
  93. pub extern "C" fn fast_blank(buf: Buf) -> bool { buf.as_slice().chars().all(|c|

    c.is_whitespace()) } *
  94. None
  95. RUST C // Rust Code extern crate libc; use std::{ptr,

    slice}; use std::marker::PhantomData; #[repr(C)] #[allow(raw_pointer_derive)] #[derive(Copy, Clone)] pub struct Buf<'a> { ptr: *mut u8, len: usize, marker: PhantomData<&'a ()>, } impl<'a> Buf<'a> { pub fn as_slice(self) -> &'a str { unsafe { let s = slice::from_raw_parts(self.ptr as *const u8, self.len); std::str::from_utf8_unchecked(s) } } } #[no_mangle] pub extern "C" fn fast_blank(buf: Buf) -> bool { buf.as_slice().unwrap().chars().all(|c| c.is_whitespace()) } // C Code #include <ruby.h> #include <ruby/encoding.h> typedef struct { void* data; size_t len; } fast_blank_buf_t; static inline fast_blank_buf_t STR2BUF(VALUE str) { return (fast_blank_buf_t) { .data = RSTRING_PTR(str), .len = RSTRING_LEN(str), }; } int fast_blank(fast_blank_buf_t); static VALUE rb_string_blank_p(VALUE str) { return fast_blank(STR2BUF(str)) ? Qtrue : Qfalse; } void Init_fast_blank() { rb_define_method(rb_cString, "blank?", rb_string_blank_p, 0); } #include <stdio.h> #include <ruby.h> #include <ruby/encoding.h> #include <ruby/re.h> #define STR_ENC_GET(str) rb_enc_from_index(ENCODING_GET(str)) static VALUE rb_string_blank(VALUE str) { rb_encoding *enc; char *s, *e; enc = STR_ENC_GET(str); s = RSTRING_PTR(str); if (!s || RSTRING_LEN(str) == 0) return Qtrue; e = RSTRING_END(str); while (s < e) { int n; unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc); switch (cc) { case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0x20: case 0x85: case 0xa0: case 0x1680: case 0x2000: case 0x2001: case 0x2002: case 0x2003: case 0x2004: case 0x2005: case 0x2006: case 0x2007: case 0x2008: case 0x2009: case 0x200a: case 0x2028: case 0x2029: case 0x202f: case 0x205f: case 0x3000: break; default: return Qfalse; } s += n; } return Qtrue; } void Init_fast_blank() { rb_define_method(rb_cString, "blank?", rb_string_blank, 0); }
  96. RUST C // Rust Code extern crate libc; use std::{ptr,

    slice}; use std::marker::PhantomData; #[repr(C)] #[allow(raw_pointer_derive)] #[derive(Copy, Clone)] pub struct Buf<'a> { ptr: *mut u8, len: usize, marker: PhantomData<&'a ()>, } impl<'a> Buf<'a> { pub fn as_slice(self) -> &'a str { unsafe { let s = slice::from_raw_parts(self.ptr as *const u8, self.len); std::str::from_utf8_unchecked(s) } } } #[no_mangle] pub extern "C" fn fast_blank(buf: Buf) -> bool { buf.as_slice().unwrap().chars().all(|c| c.is_whitespace()) } // C Code #include <ruby.h> #include <ruby/encoding.h> typedef struct { void* data; size_t len; } fast_blank_buf_t; static inline fast_blank_buf_t STR2BUF(VALUE str) { return (fast_blank_buf_t) { .data = RSTRING_PTR(str), .len = RSTRING_LEN(str), }; } int fast_blank(fast_blank_buf_t); static VALUE rb_string_blank_p(VALUE str) { return fast_blank(STR2BUF(str)) ? Qtrue : Qfalse; } void Init_fast_blank() { rb_define_method(rb_cString, "blank?", rb_string_blank_p, 0); } #include <stdio.h> #include <ruby.h> #include <ruby/encoding.h> #include <ruby/re.h> #define STR_ENC_GET(str) rb_enc_from_index(ENCODING_GET(str)) static VALUE rb_string_blank(VALUE str) { rb_encoding *enc; char *s, *e; enc = STR_ENC_GET(str); s = RSTRING_PTR(str); if (!s || RSTRING_LEN(str) == 0) return Qtrue; e = RSTRING_END(str); while (s < e) { int n; unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc); switch (cc) { case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0x20: case 0x85: case 0xa0: case 0x1680: case 0x2000: case 0x2001: case 0x2002: case 0x2003: case 0x2004: case 0x2005: case 0x2006: case 0x2007: case 0x2008: case 0x2009: case 0x200a: case 0x2028: case 0x2029: case 0x202f: case 0x205f: case 0x3000: break; default: return Qfalse; } s += n; } return Qtrue; } void Init_fast_blank() { rb_define_method(rb_cString, "blank?", rb_string_blank, 0); }
  97. HELIX

  98. #[macro_use] extern crate libcruby; declare_types! { reopen class RubyString {

    def is_blank(self) -> bool { self.chars().all(|c| c.is_whitespace()) } } }
  99. #[macro_use] extern crate libcruby; declare_types! { reopen class RubyString {

    def is_blank(self) -> bool { self.chars().all(|c| c.is_whitespace()) } } }
  100. #[macro_use] extern crate libcruby; declare_types! { reopen class RubyString {

    def is_blank(self) -> bool { self.chars().all(|c| c.is_whitespace()) } } }
  101. #[macro_use] extern crate libcruby; declare_types! { reopen class RubyString {

    def is_blank(self) -> bool { self.chars().all(|c| c.is_whitespace()) } } }
  102. #[macro_use] extern crate libcruby; declare_types! { reopen class RubyString {

    def is_blank(self) -> bool { self.chars().all(|c| c.is_whitespace()) } } }
  103. TURBO RAILS IN RUST?

  104. WHAT ABOUT MY APP?

  105. None
  106. MEAL-MATCHING ALGORITHM

  107. meal_tags = [3, 6, 18, 34, 50] preference_tags = [6,

    18, 42] meal_tags.fully_contain?(preference_tags)
  108. meal_tags = [3, 6, 18, 34, 50] preference_tags = [6,

    18, 42] meal_tags.fully_contain?(preference_tags)
  109. meal_tags = [3, 6, 18, 34, 50] preference_tags = [6,

    18, 42] meal_tags.fully_contain?(preference_tags)
  110. meal_tags = [3, 6, 18, 34, 50] preference_tags = [6,

    18, 42] meal_tags.fully_contain?(preference_tags)
  111. SET CONTAINMENT

  112. KNOWN TRICKTM

  113. class Array def fully_contain?(other) self & other == other end

    end
  114. class Array def fully_contain?(other) self & other == other end

    end
  115. class Array def fully_contain?(other) self & other == other end

    end
  116. class Array def fully_contain?(other) self & other == other end

    end
  117. SORTED

  118. UNIQUE

  119. (

  120. WE CAN DO BETTER!

  121. class Array def fully_contain?(needle) return true if needle.empty? return false

    if self.empty? needle_length = needle.length return false if needle_length > self.length needle_position = 0 needle_item = needle[needle_position] self.each do |item| if item == needle_item needle_position += 1 if needle_position >= needle_length return true else needle_item = needle[needle_position] end end end false end end
  122. class Array def fully_contain?(needle) return true if needle.empty? return false

    if self.empty? needle_length = needle.length return false if needle_length > self.length needle_position = 0 needle_item = needle[needle_position] self.each do |item| if item == needle_item needle_position += 1 if needle_position >= needle_length return true else needle_item = needle[needle_position] end end end false end end
  123. class Array def fully_contain?(needle) return true if needle.empty? return false

    if self.empty? needle_length = needle.length return false if needle_length > self.length needle_position = 0 needle_item = needle[needle_position] self.each do |item| if item == needle_item needle_position += 1 if needle_position >= needle_length return true else needle_item = needle[needle_position] end end end false end end
  124. class Array def fully_contain?(needle) return true if needle.empty? return false

    if self.empty? needle_length = needle.length return false if needle_length > self.length needle_position = 0 needle_item = needle[needle_position] self.each do |item| if item == needle_item needle_position += 1 if needle_position >= needle_length return true else needle_item = needle[needle_position] end end end false end end
  125. class Array def fully_contain?(needle) return true if needle.empty? return false

    if self.empty? needle_length = needle.length return false if needle_length > self.length needle_position = 0 needle_item = needle[needle_position] self.each do |item| if item == needle_item needle_position += 1 if needle_position >= needle_length return true else needle_item = needle[needle_position] end end end false end end
  126. UP TO 7X FASTER

  127. RUST?

  128. #[macro_use] extern crate libcruby; declare_types! { reopen class Array {

    def fully_contain(self, needle: &[usize]) -> bool { if needle.is_empty() { return true } let haystack = self.as_ref(); if haystack.is_empty() { return false } let mut needle = needle.iter(); let mut needle_item = needle.next().unwrap(); for item in haystack { if item == needle_item { match needle.next() { None => return true, Some(next_item) => needle_item = next_item } } } false } } }
  129. #[macro_use] extern crate libcruby; declare_types! { reopen class Array {

    def fully_contain(self, needle: &[usize]) -> bool { if needle.is_empty() { return true } let haystack = self.as_ref(); if haystack.is_empty() { return false } let mut needle = needle.iter(); let mut needle_item = needle.next().unwrap(); for item in haystack { if item == needle_item { match needle.next() { None => return true, Some(next_item) => needle_item = next_item } } } false } } } \
  130. #[macro_use] extern crate libcruby; declare_types! { reopen class Array {

    def fully_contain(self, needle: &[usize]) -> bool { if needle.is_empty() { return true } let haystack = self.as_ref(); if haystack.is_empty() { return false } let mut needle = needle.iter(); let mut needle_item = needle.next().unwrap(); for item in haystack { if item == needle_item { match needle.next() { None => return true, Some(next_item) => needle_item = next_item } } } false } } }
  131. #[macro_use] extern crate libcruby; declare_types! { reopen class Array {

    def fully_contain(self, needle: &[usize]) -> bool { if needle.is_empty() { return true } let haystack = self.as_ref(); if haystack.is_empty() { return false } let mut needle = needle.iter(); let mut needle_item = needle.next().unwrap(); for item in haystack { if item == needle_item { match needle.next() { None => return true, Some(next_item) => needle_item = next_item } } } false } } }
  132. #[macro_use] extern crate libcruby; declare_types! { reopen class Array {

    def fully_contain(self, needle: &[usize]) -> bool { if needle.is_empty() { return true } let haystack = self.as_ref(); if haystack.is_empty() { return false } let mut needle = needle.iter(); let mut needle_item = needle.next().unwrap(); for item in haystack { if item == needle_item { match needle.next() { None => return true, Some(next_item) => needle_item = next_item } } } false } } }
  133. #[macro_use] extern crate libcruby; declare_types! { reopen class Array {

    def fully_contain(self, needle: &[usize]) -> bool { if needle.is_empty() { return true } let haystack = self.as_ref(); if haystack.is_empty() { return false } let mut needle = needle.iter(); let mut needle_item = needle.next().unwrap(); for item in haystack { if item == needle_item { match needle.next() { None => return true, Some(next_item) => needle_item = next_item } } } false } } }
  134. #[macro_use] extern crate libcruby; declare_types! { reopen class Array {

    def fully_contain(self, needle: &[usize]) -> bool { if needle.is_empty() { return true } let haystack = self.as_ref(); if haystack.is_empty() { return false } let mut needle = needle.iter(); let mut needle_item = needle.next().unwrap(); for item in haystack { if item == needle_item { match needle.next() { None => return true, Some(next_item) => needle_item = next_item } } } false } } }
  135. #[macro_use] extern crate libcruby; declare_types! { reopen class Array {

    def fully_contain(self, needle: &[usize]) -> bool { if needle.is_empty() { return true } let haystack = self.as_ref(); if haystack.is_empty() { return false } let mut needle = needle.iter(); let mut needle_item = needle.next().unwrap(); for item in haystack { if item == needle_item { match needle.next() { None => return true, Some(next_item) => needle_item = next_item } } } false } } }
  136. UP TO 173X FASTER

  137. Ruby (Naive) Ruby (Fast) Rust

  138. ! rustbridge/helix

  139. HACK WITHOUT FEAR

  140. RUBY WITHOUT FEAR

  141. @chancancode GODFREY CHAN

  142. #MAKERUBYGREATAGAIN