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

Useful Rust

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

Useful Rust

Avatar for Florian Gilcher

Florian Gilcher

October 25, 2016
Tweet

More Decks by Florian Gilcher

Other Decks in Programming

Transcript

  1. • Backend Developer • Ruby Programmer since 2003 • Rust

    Programmer since 2013 • CEO asquera GmbH
  2. • Community person • Rust UG Berlin/Karlsruhe • Search UG

    Berlin • Ex-chairman of Ruby Berlin e.V.
  3. • Part of the global Rust community team • Organiser

    eurucamp/jrubyconf.eu • Organiser RustFest
  4. Ownership struct Meal { name: String } fn eat(m: Meal)

    { println!("Ate: {}", m.name); // m will be destroyed here }
  5. fn eat(m: Meal) { let mut mutated = m; mutated.name

    = String::from("Godzilla & Chips"); println!("Ate: {}", mutated.name); drop(m) }
  6. impl Edible for Meal { fn eat(self) { println!("Ate: {}",

    self.name); } fn describe(&self) { println!("This is: {}", self.name); } }
  7. fn main() { let meal = Meal { name: String::from("Fish

    & Chips") }; meal.describe(); meal.eat(); meal.eat(); //~^ ERROR use of moved value: `meal` }
  8. “ Hey, describe me the data in the way we

    both know best, and I will derive the rest.”
  9. extern crate serde_json; // define MyApiMessage fn main() { use

    serde_json::from_str; let res = from_str::<MyApiMessage>("..."); }
  10. • Uses internal type information • Fails early if the

    received message is unknown • Does not raise exceptions
  11. fn main() { use serde_json::from_str; let res = from_str::<MyApiMessage>("..."); match

    res { Ok(message) => ..., Err(e) => println!("Error: {}", e) } }
  12. fn main() { use serde_json::from_str; let res = from_str::<MyApiMessage>("...") .and_then(|message|{

    MyDomainModel::from(message) }) //~^ WARNING: unused result which must be used }
  13. fn main() { use serde_json::from_str; let res = from_str::<MyApiMessage>("...") .and_then(|message|{

    MyDomainModel::from(message) }) //~^ WARNING: unused result which must be used }
  14. It’s not unusual for libraries to have test suites for

    the cases that should error/warn at complain time.
  15. It’s not unusual for libraries to have test suites for

    the cases that should error/warn at compile time.
  16. Inconvenient • Initialisation depending on exact structure • Struct fields

    are by default private, this is not going to work outside of the defining module.
  17. Seperation of functions and state impl Replicator { pub fn

    new(s: String, t: String) -> Replicator { Replicator { source: s, target: t } } }
  18. impl Replicator { pub fn new<S,T>(source: S, target: T) ->

    Replicator where S: Into<String>, T: Into<String> { Replicator { source: source.into(), target: target.into() } } }
  19. The VerifyPeers steps Given a replicator between two databases: •

    Check source existence • Check target existence • Create target if absent • Done
  20. Defining states pub struct Start; pub struct SourceExisting; pub struct

    TargetExisting; pub struct TargetAbsent; pub struct VerifiedPeers;
  21. impl<S> VerifyPeers<S> { pub fn transition<X>(self, new_state: X) -> VerifyPeers<X>

    { VerifyPeers { replicator: self.replicator, state: new_state } } }
  22. let replicator = Replicator::new("source_name", "target_name"); let verification = VerifyPeers::new(replicator); let

    end = verification .transition(SourceExisting) .transition(TargetExisting) .transition(VerifiedPeers);
  23. let replicator = Replicator::new("source_name", "target_name"); let verification = VerifyPeers::new(replicator); let

    end = verification .transition(SourceExisting) .transition(TargetExisting) .transition(VerifiedPeers); verification.transition(SourceExisting); //~^ ERROR use of moved value: `verification`
  24. Nonsensical transitions struct Counter { count: i32 } fn main()

    { let r = Replicator::new("source", "target"); let v = VerifyPeers::new(r); let counter = Counter { count: 0 }; let end = v .transition(counter); }
  25. Marking states pub trait State {} impl State for Start

    {} impl State for SourceExisting {} impl State for TargetExisting {} impl State for TargetAbsent {} impl State for VerifiedPeers {}
  26. Constraining to States impl<S: State> VerifyPeers<S> { pub fn transition<X>(self,

    new_state: X) -> VerifyPeers<X> where X: State { VerifyPeers { replicator: self.replicator, state: new_state } } }
  27. Now failing struct Counter { count: i32 } fn main()

    { let r = Replicator::new("source", "target"); let v = VerifyPeers::new(r); let counter = Counter { count: 0 }; let end = v .transition(counter); }
  28. We implement in the same fashion: • SourceExisting -> TargetAbsent

    • SourceExisting -> TargetExisting • TargetAbsent -> TargetExisting • TargetExisting -> VerifiedPeers
  29. impl<S: State> VerifyPeers<S> { pub fn transition<X>(self, new_state: X) ->

    VerifyPeers<X> where X: State + From<S> { VerifyPeers { replicator: self.replicator, state: new_state } } }
  30. Now failing fn main() { let r = Replicator::new("source", "target");

    let v = VerifyPeers::new(r); let end = v .transition(VerifiedPeers); }
  31. “ I was promised Useful Rust and all I got

    was a lousy state machine!”
  32. impl VerifyPeers<Start> { pub fn check_source_existence(self) -> Result<VerifyPeers<SourceExisting>, Error> {

    if try!(check_source_existence()) { Ok(self.transition(SourceExisting)) } else { Err("Source doesn’t exist!".into()) } } }
  33. How do we implement a branch? pub enum TargetChecked {

    TargetExists(VerifyPeers<TargetExisting>), TargetAbsent(VerifyPeers<TargetAbsent>) }
  34. impl VerifyPeers<SourceExisting> { pub fn check_target_existence(self) -> Result<TargetChecked, Error> {

    if try!(target_exists()) { Ok(self.transition(TargetExisting)) } else { Ok(self.transition(TargetAbsent)) } } }
  35. extern crate statemachines; use statemachines::*; fn main() { let r

    = Replicator::new("source", "target"); let v = VerifyPeers::new(r); let end = v .check_source_existence() .and_then(|state| { state.check_target_existence() }).and_then(|state| { //... }
  36. .and_then(|state| { match state { TargetChecked::TargetExists(s) => { s.finish() }

    TargetChecked::TargetAbsent(s) => { try!(s.create_target()).finish() } } });
  37. A bit of low-level #[test] fn verify_peers_not_larger_then_replicator() { use std::mem::size_of;

    assert_eq!(size_of::<VerifyPeers<Start>>(), size_of::<Replicator>()) }
  38. • This pattern extends well and also works with, e.g.

    Futures instead of Results • Other implementations are possible
  39. Rust is focused on usability • Usability is tough if

    you want fine- grained control for the user • In systems programming, usability is not champion