Slide 1

Slide 1 text

Hello Rust An Introduction + Ruby Extensions in Rust

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Memory Unsafe Memory Safe Low-Level Pointers C, C++, Objective C Garbage Collected Ruby, Java, Go

Slide 5

Slide 5 text

Memory Unsafe Memory Safe Low-Level Pointers C, C++, Objective C Garbage Collected Ruby, Java, Go

Slide 6

Slide 6 text

Memory Unsafe Memory Safe Low-Level Pointers C, C++, Objective C Garbage Collected Ruby, Java, Go Rust

Slide 7

Slide 7 text

Memory Unsafe Memory Safe Low-Level Pointers C, C++, Objective C Garbage Collected Objective C Ruby, Java, Go Rust

Slide 8

Slide 8 text

Memory Unsafe Memory Safe Low-Level Pointers C, C++, Objective C Garbage Collected Objective C Ruby, Java, Go Rust

Slide 9

Slide 9 text

Why Low Level? • Directly control memory usage • Avoid uncontrollable GC pauses • Embed into other GC'ed environments (like Ruby!)

Slide 10

Slide 10 text

Why Safe? • SEGVs are BAD!

Slide 11

Slide 11 text

How Does it Work? Ownership!

Slide 12

Slide 12 text

Ownership struct  Point  {  x:  int,  y:  int  }
 struct  Line    {  a:  Point,  b:  Point  }
 
 fn  main()  {
    let  a  =  Point{  x:  10,  y:  10  };
    let  b  =  Point{  x:  20,  y:  20  };
 } owned by main()

Slide 13

Slide 13 text

Ownership struct  Point  {  x:  int,  y:  int  }
 struct  Line    {  a:  Point,  b:  Point  }
 
 fn  main()  {
    let  a  =  Point{  x:  10,  y:  10  };
    let  b  =  Point{  x:  20,  y:  20  };
    
    let  line  =  Line{  a:  a,  b:  b  }
 } copied into line owned by main()

Slide 14

Slide 14 text

Ownership struct  Point  {  x:  int,  y:  int  }
 struct  Line    {  a:  Point,  b:  Point  }
 
 fn  main()  {
    let  a  =  Point{  x:  10,  y:  10  };
    let  b  =  Point{  x:  20,  y:  20  };
    
    let  line  =  Line{  a:  a,  b:  b  }
 } owned by main() owned by main() line is dropped with its contents; a and b are dropped

Slide 15

Slide 15 text

"Stack Allocation" is Cool

Slide 16

Slide 16 text

Stack Allocation struct  Point  {  x:  int,  y:  int  }
 struct  Line    {  a:  Point,  b:  Point  }
 
 fn  main()  {
    let  a  =  Point{  x:  10,  y:  10  };
    let  b  =  Point{  x:  20,  y:  20  };
    
    let  line  =  Line{  a:  a,  b:  b  }
 } 128 bits 256 bits allocate 512 bits for stack storage

Slide 17

Slide 17 text

Ownership struct  Point  {  x:  int,  y:  int  }
 struct  Line    {  a:  Point,  b:  Point  }
 
 fn  main()  {
    let  a  =  Point{  x:  10,  y:  10  };
    let  b  =  Point{  x:  20,  y:  20  };
    
    let  line  =  Line{  a:  a,  b:  b  }
 } owned by this scope

Slide 18

Slide 18 text

Ownership struct  Point  {  x:  int,  y:  int  }
 struct  Line    {  a:  Point,  b:  Point  }
 
 fn  main()  {
    let  a  =  Point{  x:  10,  y:  10  };
    let  b  =  Point{  x:  20,  y:  20  };
    
    let  line  =  Line{  a:  a,  b:  b  }
 } moving?!

Slide 19

Slide 19 text

Ownership struct  Point  {  x:  int,  y:  int  }
 struct  Line    {  a:  ~Point,  b:  ~Point  }
 
 fn  main()  {
    let  a  =  ~Point{  x:  10,  y:  10  };
    let  b  =  ~Point{  x:  20,  y:  20  };
    
    let  line  =  ~Line{  a:  a,  b:  b  }
 } owned by main(), moveable

Slide 20

Slide 20 text

Ownership struct  Point  {  x:  int,  y:  int  }
 struct  Line    {  a:  ~Point,  b:  ~Point  }
 
 fn  main()  {
    let  a  =  ~Point{  x:  10,  y:  10  };
    let  b  =  ~Point{  x:  20,  y:  20  };
    
    let  line  =  ~Line{  a:  a,  b:  b  }
 } heap allocated

Slide 21

Slide 21 text

Computations use  std::num::{pow,sqrt};
 
 struct  Point  {  x:  int,  y:  int  }
 struct  Line    {  a:  ~Point,  b:  ~Point  }
 
 fn  length(line:  ~Line)  -­‐>  uint  {
    let  Line{  a,  b  }  =  line;
    let  x  =  pow(b.x  -­‐  a.x,  2);
    let  y  =  pow(b.y  -­‐  a.y,  2);
    sqrt(x  +  y)
 } owned by length() line is dropped

Slide 22

Slide 22 text

But Wait! I can only do one computation with a struct?!

Slide 23

Slide 23 text

Computations use  std::num::{pow,sqrt};
 
 struct  Point  {  x:  int,  y:  int  }
 struct  Line    {  a:  Point,  b:  Point  }
 
 fn  length(line:  &Line)  -­‐>  uint  {
  let  x  =  pow(line.b.x  -­‐  line.a.x,  2);
    let  y  =  pow(line.b.y  -­‐  line.a.y,  2);
    sqrt(x  +  y)
 } borrowed by length() line is not dropped moving is disallowed by the compiler

Slide 24

Slide 24 text

Stack Allocation is Back! fn  main()  {
    let  line  =  Line{  a:  Point{  x:  10,  y:  10  },  b:  Point{  x:  20,  y:  20  }  };
    println!("length:  {:u}",  length(&mut  line));
 }
 
 fn  length(line:  &mut  Line)  -­‐>  uint  {
    let  x  =  pow(line.b.x  -­‐  line.a.x,  2);
    let  y  =  pow(line.b.y  -­‐  line.a.y,  2);
    sqrt(x  +  y)
 } line is not dropped moving is disallowed by the
 compiler, so passing a stack
 value's memory address is safe

Slide 25

Slide 25 text

Object Orientation, Can Haz? use  std::num::{pow,sqrt};
 
 struct  Point  {  x:  int,  y:  int  }
 struct  Line    {  a:  Point,  b:  Point  }
 
 impl  Line  {
    fn  length(&self)  -­‐>  uint  {
        let  x  =  pow(self.b.x  -­‐  self.a.x,  2);
        let  y  =  pow(self.b.y  -­‐  self.a.y,  2);
        sqrt(x  +  y)
    }
 } self is almost always borrowed

Slide 26

Slide 26 text

Net Result • You never have to malloc or free • You never have to retain or release • Rust will deallocate a value when the current owner is done with it • The compiler will guarantee that borrowed values are not stolen

Slide 27

Slide 27 text

Normal Idioms • Prefer stack allocation to heap allocation if you can get away with it • Usually borrow values (&T visually blends away, ~T is an outlier) • Mutability is explicit (&T vs. &mut  T) • OO methods virtually always take &self or &mut  self

Slide 28

Slide 28 text

Ownership and Leaks fn  main() Line Point int int Point int int

Slide 29

Slide 29 text

Ownership and Leaks

Slide 30

Slide 30 text

Ownership and Leaks fn  main() ~Line ~Point int int ~Point int int

Slide 31

Slide 31 text

fn  length() Ownership and Leaks fn  main() ~Line ~Point int int ~Point int int

Slide 32

Slide 32 text

fn  length() Ownership and Leaks fn  main() ~Line ~Point int int ~Point int int

Slide 33

Slide 33 text

Ownership and Leaks fn  main()

Slide 34

Slide 34 text

Ownership and Leaks

Slide 35

Slide 35 text

Task Local Storage Because it's "permanent", moving ~T
 into it can cause leaks

Slide 36

Slide 36 text

Long-Lived Loops Long-lived loops may contain
 ever-growing Arrays that "leak"

Slide 37

Slide 37 text

It's never "I forgot to free"

Slide 38

Slide 38 text

Traits

Slide 39

Slide 39 text

Area struct  Square  {  width:  f32  }
 struct  Circle  {  radius:  f32  }
 
 trait  Shape  {
    fn  area(&self)  -­‐>  f32;
 }
 
 impl  Shape  for  Square  {
    fn  area(&self)  -­‐>  f32  {  self.width  *  self.width  }
 }
 
 impl  Shape  for  Circle  {
    fn  area(&self)  -­‐>  f32  {  pow(3.14  *  self.radius,  2)  }
 }

Slide 40

Slide 40 text

Usage use  mylib::{Shape,Circle,Square};
 
 fn  area(shape:  &T)  -­‐>  f32  {
    shape.area()
 }
 
 fn  main()  {
    let  circle  =  Circle{  radius:  5  };
    let  square  =  Square{  width:  10  };
    println!("incribed  circle:  {}",  area(&circle)  -­‐  area(&square));
 }

Slide 41

Slide 41 text

Usage use  mylib::{Shape,Circle,Square};
 
 fn  area(shape:  &T)  -­‐>  f32  {
    shape.area()
 }
 
 fn  main()  {
    let  circle  =  Circle{  radius:  5  };
    let  square  =  Square{  width:  10  };
    println!("incribed  circle:  {}",  area(&circle)  -­‐  area(&square));
 }   any type that
 implements Shape

Slide 42

Slide 42 text

Generates, Under the Hood use  mylib::{Shape,Circle,Square};
 
 fn  circle_area(shape:  &Circle)  -­‐>  f32  {
    shape.area()
 }
 
 fn  square_area(shape:  &Square)  -­‐>  f32  {
    shape.area()
 }
 
 fn  main()  {
    let  circle  =  Circle{  radius:  5  };
    let  square  =  Square{  width:  10  };
    println!("incribed  circle:  {:?}",  circle_area(&circle)  -­‐  square_area(&square));
 }

Slide 43

Slide 43 text

Operator Overloading struct  Point  {  x:  int,  y:  int  }
 
 impl  Add  for  Point  {
    fn  add(&self,  other:  &Point)  -­‐>  Point  {
        Point{  x:  self.x  +  other.x,  y:  self.y  +  other.y  }
    }
 }
 
 fn  log_add(first:  &T,  second:  &T)  {
    println!("{:?}",  first  +  second);
 }

Slide 44

Slide 44 text

http://bit.ly/rust-go-http

Slide 45

Slide 45 text

http://bit.ly/rust-go-http use  http::{Request,Response,HttpErr};   use  http::status::Found;       fn  load_page(title:  &str)  -­‐>  Page  {      let  body  =  try!(File::read(str));      Page::new(title,  body)   }       fn  view(res:  &mut  Response,  req:  &Request)  {      match  load_page(req.params["id"])  {          Err(_)  =>              res.redirect("/edit/"  +  title,  Found),          Ok(page)  =>
            render_template(res,  "view",  page)      }   }   
 
 
 
 ! fn  edit(res:  &mut  Response,  req:  &Request)  {      let  title  =  req.params["id"];                let  page  =  match  load_page(title)  {          Err(_)  =>  Page{  title:  title  },          Ok(page)  =>  page      }                render_template(res,  "edit",  page);   }       fn  save(res:  &mut  Response,  req:  &Request)  {      let  title  =  req.params["id"];      let  body  =  try!(req.params["body"]);          let  page  =  try!(Page::new(title,  
        body.as_bytes()).save());
 !    res.redirect("/view/"  +  title,  Found);   }

Slide 46

Slide 46 text

http://bit.ly/rust-go-http import  (          "html/template",          "io/ioutil",          "net/http"   )   ! func  loadPage(title  string)  *Page  {          filename  :=  title  +  ".txt"          body,  _  :=  ioutil.ReadFile(filename)          return  &Page{Title:  title,  Body:  body}   }   ! func  getTitle(w  http.ResponseWriter,  r  *http.Request)  (string,  error)  {          m  :=  validPath.FindStringSubmatch(r.URL.Path)          if  m  ==  nil  {                  http.NotFound(w,  r)                  return  "",  errors.New("Invalid  Page  Title")          }          return  m[2],  nil  //  The  title  is  the  second  subexpression.   }   ! func  viewHandler(w  http.ResponseWriter,  r  *http.Request)  {          title,  err  :=  getTitle(w,  r)          if  err  !=  nil  {                  return          }          p,  err  :=  loadPage(title)          if  err  !=  nil  {                  http.Redirect(w,  r,  "/edit/"+title,  http.StatusFound)                  return          }          renderTemplate(w,  "view",  p)   }   
 func  editHandler(w  http.ResponseWriter,  r  *http.Request)  {          title,  err  :=  getTitle(w,  r)          if  err  !=  nil  {                  return          }          p,  err  :=  loadPage(title)          if  err  !=  nil  {                  p  =  &Page{Title:  title}          }          renderTemplate(w,  "edit",  p)   }   ! func  saveHandler(w  http.ResponseWriter,  r  *http.Request)  {          title,  err  :=  getTitle(w,  r)          if  err  !=  nil  {                  return          }          body  :=  r.FormValue("body")          p  :=  &Page{Title:  title,  Body:  []byte(body)}          err  =  p.save()          if  err  !=  nil  {                  http.Error(w,  err.Error(),  http.StatusInternalServerError)                  return          }          http.Redirect(w,  r,  "/view/"+title,  http.StatusFound)   }

Slide 47

Slide 47 text

Ruby C Extension

Slide 48

Slide 48 text

The Basics struct  Trace  {
    name:  ~str
 }
 
 #[no_mangle]
 pub  extern  "C"  fn  skylight_get_name(trace:  &Trace)  -­‐>  ~str  {
    trace.name.clone()
 }

Slide 49

Slide 49 text

The Basics #[no_mangle]
 pub  extern  "C"  fn  skylight_get_name(trace:  &Trace)  -­‐>  ~str  {
    trace.name.clone()
 }   VALUE  trace_get_name(VALUE  self)  {
    RustTrace  trace;
    Data_Get_Struct(self,  RustTrace,  trace);
    return  RUST2STR(skylight_get_name(trace));
 }

Slide 50

Slide 50 text

Memory #[no_mangle]
 pub  extern  "C"  fn  skylight_free_str(_:  ~str)  {}   #define  RUST2STR(string)  ({                                                    \
    RustString  s  =  (string);                                                      \
    VALUE  ret  =  rb_str_new((char  *)s-­‐>data,  s-­‐>fill);    \
    skylight_free_str(s);                                                            \
    ret;                                                                                              \
 })                                                                                                      \

Slide 51

Slide 51 text

Many Details • Catching failures at the boundary • Freeing objects idiomatically on the C side • Invalidating C references involved in a failure • ...

Slide 52

Slide 52 text

Skylight's Library ffi_fn!(skylight_trace_get_name(trace:  &'a  Trace)  -­‐>  &'a  str  {      trace.get_name()   })   ! static  VALUE  trace_get_name(VALUE  self)  {      My_Struct(trace,  RustTrace,  freedTrace);   !    RustSlice  string;      return  CHECK_FFI(skylight_trace_get_name(trace,  &string),
        "Could  not  get  the  name  from  a  Trace  in  native  code");   }

Slide 53

Slide 53 text

ffi_fn! • A Rust macro (yay macros!) • Synthesizes a Rust task and catches failures • Returns a bool to indicate success or failure • Uses an out variable for the return value

Slide 54

Slide 54 text

On the C-Side • RUST2STR copies Rust strings into Ruby strings • typedef  void  *  RustTrace • CHECK_FFI turns a false return into a Ruby exception • CHECK_TYPE and CHECK_NUMERIC protect the boundary • Transfer_My_Struct nulls out an object's struct if passed as ~T   • Learn FIX2INT, INT2FIX, ULL2NUM, NUM2ULL, etc.

Slide 55

Slide 55 text

rust.rb COMING SOON!

Slide 56

Slide 56 text

Questions? @wycats plus.yehudakatz.com yehudakatz.com