Ruby: It’s Really Not That Bad # This is good: Person.walk_the_dog(Dog.new) # But WHAT IF? Person.walk_the_dog(Cat.new) # Solution: don’t do that ¯\_( ツ )_/¯
enum Result { Ok(T), Err(E), } fn main() { // settings: Result let settings = fs::read_to_string("~/settings.json"); // But why not a pair (String, io::Error)? let (settings, error) = fs::read_to_string("~/settings.json"); }
# Successful result: Result.new(value: "Pumpkin spice latte") # Errorful result: Result.new(error: "Ran out of pumpkins!") result. or_else { |e| ... }. and_then { |v| ... } # Is this possible? :/ What do we do here? Result.new( value: "Everything is great!", error: "Everything is broken!" )
class Success def and_then(&block) block.call(value) end def or_else(&block) # do nothing end end class Error def and_then(&block) # do nothing end def or_else(&block) block.call(error) end end
result = Result.new(value: [], error: []) ComplicatedProcess.each do |individual_result| if individual_result.success? result.value << individual_result else result.error << "Something went wrong with #{individual_result}" end end # (Basic usage of `Result`, no `and_then`/`or_else`) # Temporary/intermediate step – a patch
# In code class UserRegistration def facebook_client FacebookClient.new end end # In tests class UserRegistration # Patch it! def facebook_client FakeFacebookClient.new end end
require 'active_support/core_ext/string/inflections' "table".pluralize # => "tables" "ox".pluralize # => "oxen" # String#pluralize is now *everywhere* # (Refinements are a possible solution)
fn main() { let pattern = Regex::new(r"\S+@\S+\.\S+").unwrap(); let input = Article::new( "Hello!", r#" My email is [email protected]! I've got another one at [email protected]! "# ); let is_match: bool = input.search(&pattern); let actual_match: Option = input.search(&pattern); let all_the_matches: Vec = input.search(&pattern); }
fn main() { let pattern = Regex::new(r"\S+@\S+\.\S+").unwrap(); let input = Article::new( "Hello!", r#" My email is [email protected]! I've got another one at [email protected]! "# ); if input.search(&pattern) { println!("Yes, a match!"); } if let Some(m) = input.search(&pattern) { println!("Yes, a match: {}!", m); } }
fn main() { let pattern = Regex::new(r"\S+@\S+\.\S+").unwrap(); let input = Article::new( "Hello!", r#" My email is [email protected]! I've got another one at [email protected]! "# ); let all_the_matches: Vec = input.search(&pattern); for m in all_the_matches { println!("One of the matches is: {}", m); } }
impl From for AppError { /*...*/ } impl From for AppError { /*...*/ } impl From for Target { // ... } impl<...> Into for Source where Target: From { fn into(self) -> Target { Target::from(self) } } // AppError::from(e) == e.into()
Return-type polymorphism ✔It’s magic! ✔Extra degree of freedom in interface design ✔Control over the “shape” of code ✘ It’s magic :/ ✘ Can hurt understanding
GTK-rs There are two reasons behind going back on this feature: The first one is about [...] error messages. [...] annoying situations where you had to try to figure out what was going on. The second one is to go back to some Rust fundamentals: being explicit. […] – The GTK-rs team
The freedom to make mistakes fn schroedingers_cat() -> Box; // This makes sense: fn schroedingers_cat() -> Box { } fn schroedingers_cat() -> Box { } // But WHAT IF? fn schroedingers_cat() -> Box { } // Don’t do that ;) ;) ;)