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

Useful Rust

Useful Rust

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