Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Type System Tricks for the Real World

Type System Tricks for the Real World

Have you ever looked at some of the more exotic capabilities of Rust's type system and wondered “why”? Why on earth would I ever want a zero sized type? How do I efficiently implement a deeply recursive type? Just what do monomorphization and type erasure actually mean?

In this talk we'll look at some real world examples from inside Diesel to answer these questions and more. You'll come away from this talk with a stronger understanding of how to use Rust's generics, traits, and exotically sized types.

Sean Griffin

August 19, 2017
Tweet

More Decks by Sean Griffin

Other Decks in Technology

Transcript

  1. Who am I? • Sean Griffin • 10x Hacker Ninja

    Guru at Shopify • Rails Committer • Maintainer of Active Record • Creator of Diesel • Bikeshed co-host
  2. pub struct HashSet<K> { map: HashMap<K, ()> } impl<K: Hash

    + Eq> HashSet<K> { pub fn insert(&mut self, value: K) { self.map.insert(value, ()); } pub fn contains(&self, value: &K) -> bool { self.map.contains_key(value) } }
  3. class Set def initialize @hash = {} end def insert(value)

    @hash[value] = true end def contains(value) @hash.key?(value) end end
  4. error[E0072]: recursive type `ListString` has infinite size --> src/main.rs:1:1 |

    1 | enum ListString { | ^^^^^^^^^^^^^^^ recursive type has infinite size ... 4 | tail: ListString, | ---------------- recursive without indirection | = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `ListString` representable
  5. struct Pinapple; struct Pizza<Topping> { topping: Topping } size_of::<Pizza<Pinapple>>() //

    => Error: // Pineapple doesn't go on pizza, Steve. Fite me.
  6. Nil

  7. fn string_contains_a(s: ListString) -> bool { match s { Cons

    { head: 'a', .. } => true, Cons { tail, .. } => string_contains_a(tail), Nil => false, } }
  8. fn string_contains_a<T: ListString>(s: T) -> bool { match s.unpack() {

    Some(('a', _)) => true, Some((_, tail)) => string_contains_a(tail), None => false, } }
  9. trait Robot { fn username(&self) -> &str; } fn say_hi<T:

    Robot>(robot: &T) { println!("Hi, {}", robot.username()); }
  10. struct Bors; impl Robot for Bors { fn username(&self) ->

    &str { "@bors" } } struct Alex; impl Robot for Alex { fn username(&self) -> &str { "@alexcrichton" } }
  11. struct Bors; impl Robot for Bors { fn username(&self) ->

    &str { "@bors" } } struct Alex; impl Robot for Alex { fn username(&self) -> &str { "@alexcrichton" } }
  12. struct Bors; fn Robot_username_Bors(_: &Bors) -> &str { "@bors" }

    struct Alex; impl Robot for Alex { fn username(&self) -> &str { "@alexcrichton" } }
  13. struct Bors; fn Robot_username_Bors(_: &Bors) -> &str { "@bors" }

    struct Alex; impl Robot for Alex { fn username(&self) -> &str { "@alexcrichton" } }
  14. struct Bors; fn Robot_username_Bors(_: &Bors) -> &str { "@bors" }

    struct Alex; fn Robot_username_Alex(_: &Alex) -> &str { "@alexcrichton" }
  15. fn say_hi_Bors(_: &Bors) { println!("Hi, {}", "@bors"); } fn say_hi_Alex(alex:

    &Alex) { println!("Hi, {}", Robot_username_Alex(alex)); }
  16. fn say_hi_Bors(_: &Bors) { println!("Hi, {}", "@bors"); } fn say_hi_Alex(alex:

    &Alex) { println!("Hi, {}", Robot_username_Alex(alex)); }
  17. fn say_hi_Bors(_: &Bors) { println!("Hi, {}", "@bors"); } fn say_hi_Alex(_:

    &Alex) { println!("Hi, {}", "@alexcrichton"); }
  18. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); self.distinct.walk_ast(out)?;

    self.select.walk_ast(&self.from, out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  19. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); self.distinct.walk_ast(out)?;

    self.select.walk_ast(&self.from, out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  20. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); self.distinct.walk_ast(out)?;

    self.select.walk_ast(&self.from, out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  21. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); NoDistinctClause::walk_ast(out)?;

    self.select.walk_ast(&self.from, out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  22. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); Ok(())?;

    self.select.walk_ast(&self.from, out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  23. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); self.select.walk_ast(&self.from,

    out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  24. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); self.select.walk_ast(&self.from,

    out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  25. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); DefaultSelectClause::walk_ast(&self.from,

    out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  26. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); self.from.default_selection()

    .walk_ast(out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  27. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); self.from.default_selection()

    .walk_ast(out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  28. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); (users::id,

    users::name) .walk_ast(out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  29. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); (users::id,

    users::name) .walk_ast(out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  30. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); if

    0 != 0 { out.push_sql(", "); } users::id.walk_ast(out)?; if 1 != 0 { out.push_sql(", "); } users::name.walk_ast(out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?;
  31. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); if

    0 != 0 { out.push_sql(", "); } users::id.walk_ast(out)?; if 1 != 0 { out.push_sql(", "); } users::name.walk_ast(out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?;
  32. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); if

    false { out.push_sql(", "); } users::id.walk_ast(out)?; if 1 != 0 { out.push_sql(", "); } users::name.walk_ast(out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?;
  33. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); if

    false { out.push_sql(", "); } users::id.walk_ast(out)?; if 1 != 0 { out.push_sql(", "); } users::name.walk_ast(out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?;
  34. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); users::id.walk_ast(out)?;

    if 1 != 0 { out.push_sql(", "); } users::name.walk_ast(out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  35. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); users::id.walk_ast(out)?;

    if 1 != 0 { out.push_sql(", "); } users::name.walk_ast(out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  36. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); users::id.walk_ast(out)?;

    if true { out.push_sql(", "); } users::name.walk_ast(out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  37. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); users::id.walk_ast(out)?;

    if true { out.push_sql(", "); } users::name.walk_ast(out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  38. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); users::id.walk_ast(out)?;

    out.push_sql(", "); users::name.walk_ast(out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  39. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); users::id.walk_ast(out)?;

    out.push_sql(", "); users::name.walk_ast(out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  40. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); users::table.walk_ast(out)?;

    out.push_sql("."); out.push_identifier("id"); out.push_sql(", "); users::name.walk_ast(out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  41. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); users::table.walk_ast(out)?;

    out.push_sql("."); out.push_identifier("id"); out.push_sql(", "); users::name.walk_ast(out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  42. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); out.push_identifier("users");

    out.push_sql("."); out.push_identifier("id"); out.push_sql(", "); users::name.walk_ast(out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  43. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); out.push_sql("\"");

    out.push_sql(&"users".replace('"', "\"\"")); out.push_sql("\""); out.push_sql("."); out.push_identifier("id"); out.push_sql(", "); users::name.walk_ast(out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?;
  44. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); out.push_sql("\"");

    out.push_sql(&"users".replace('"', "\"\"")); out.push_sql("\""); out.push_sql("."); out.push_identifier("id"); out.push_sql(", "); users::name.walk_ast(out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?;
  45. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); out.push_sql("\"");

    out.push_sql("users"); out.push_sql("\""); out.push_sql("."); out.push_identifier("id"); out.push_sql(", "); users::name.walk_ast(out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?;
  46. out.push_sql("name"); out.push_sql("\""); out.push_sql(" FROM "); out.push_sql("\""); out.push_sql("users"); out.push_sql("\""); out.push_sql(" WHERE

    "); self.where_clause.0.left.walk_ast(out)?; out.push_sql(" = "); self.where_clause.0.right.walk_ast(out)?; Ok(()) }
  47. out.push_sql("name"); out.push_sql("\""); out.push_sql(" FROM "); out.push_sql("\""); out.push_sql("users"); out.push_sql("\""); out.push_sql(" WHERE

    "); self.where_clause.0.left.walk_ast(out)?; out.push_sql(" = "); self.where_clause.0.right.walk_ast(out)?; Ok(()) }
  48. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); self.distinct.walk_ast(out)?;

    self.select.walk_ast(&self.from, out)?; out.push_sql(" FROM "); self.from.walk_ast(out)?; self.where_clause.walk_ast(out)?; Ok(()) }
  49. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); out.push_sql("\"");

    out.push_sql("users"); out.push_sql("\""); out.push_sql("."); out.push_sql("\""); out.push_sql("id"); out.push_sql("\""); out.push_sql(", "); out.push_sql("\""); out.push_sql("users"); out.push_sql("\""); out.push_sql("."); out.push_sql("\""); out.push_sql("name"); out.push_sql("\""); out.push_sql(" FROM "); out.push_sql("\""); out.push_sql("users"); out.push_sql("\""); out.push_sql(" WHERE "); out.push_sql("\""); out.push_sql("users"); out.push_sql("\""); out.push_sql("."); out.push_sql("\""); out.push_sql("id"); out.push_sql("\""); out.push_sql(" = "); out.push_bind_param(&self.where_clause.0.right.item, out)?; Ok(()) }
  50. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { out.push_sql("SELECT "); out.push_sql("\"");

    out.push_sql("users"); out.push_sql("\""); out.push_sql("."); out.push_sql("\""); out.push_sql("id"); out.push_sql("\"");
  51. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { if let AstPass::ToSql(ref

    mut builder) = out { builder.push_sql("SELECT "); } out.push_sql("\""); out.push_sql("users"); out.push_sql("\""); out.push_sql("."); out.push_sql("\""); out.push_sql("id"); out.push_sql("\"");
  52. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { let AstPass::ToSql(ref mut

    builder) = out; builder.push_sql("SELECT "); out.push_sql("\""); out.push_sql("users"); out.push_sql("\""); out.push_sql("."); out.push_sql("\""); out.push_sql("id"); out.push_sql("\"");
  53. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { let AstPass::ToSql(ref mut

    builder) = out; builder.push_sql("SELECT "); out.push_sql("\""); out.push_sql("users"); out.push_sql("\""); out.push_sql("."); out.push_sql("\""); out.push_sql("id"); out.push_sql("\"");
  54. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { let AstPass::ToSql(ref mut

    builder) = out; builder.sql.push_str("SELECT "); out.push_sql("\""); out.push_sql("users"); out.push_sql("\""); out.push_sql("."); out.push_sql("\""); out.push_sql("id"); out.push_sql("\"");
  55. fn walk_ast(&self, out: AstPass) -> QueryResult<()> { let AstPass::CollectBinds {

    collector, metadata_lookup } = out; collector.push_bound_value( &self.where_clause.0.right.value, metadata_lookup, )?; Ok(()) }
  56. pub struct HashSet<K> { map: HashMap<K, ()> } impl<K: Hash

    + Eq> HashSet<K> { pub fn insert(&mut self, value: K) { self.map.insert(value, ()); } pub fn contains(&self, value: &K) -> bool { self.map.contains_key(value) } }
  57. pub struct HashSet<K> { map: HashMap<K, ()> } impl<K: Hash

    + Eq> HashSet<K> { pub fn insert(&mut self, value: K) { self.map.insert(value, ()); } pub fn contains(&self, value: &K) -> bool { self.map.contains_key(value) } }