map for Result Panicking in the previous example's multiply does not make for robust code. Generally, we want to return the error to the caller so it can decide what is the right way to respond to errors. We first need to know what kind of...
Result Result is a richer version of the Option type that describes possible error instead of possible absence. That is, Result<T, E> could have one of two outcomes: • Ok(T): An element T was found • Err(E): An error was found with element...
Unpacking options and defaults There is more than one way to unpack an Option and fall back on a default if it is None. To choose the one that meets our needs, we need to consider the following: • do we need eager or lazy evaluation? • do w...
Combinators: and_then map() was described as a chainable way to simplify match statements. However, using map() on a function that returns an Option<T> results in the nested Option<Option<T>>. Chaining multiple calls together can then becom...
Combinators: map match is a valid method for handling Options. However, you may eventually find heavy usage tedious, especially with operations only valid with an input. In these cases, combinators can be used to manage control flow in a mo...
Unpacking options with ? You can unpack Options by using match statements, but it's often easier to use the ? operator. If x is an Option, then evaluating x? will return the underlying value if x is Some, otherwise it will terminate whateve...
Option & unwrap In the last example, we showed that we can induce program failure at will. We told our program to panic if we drink a sugary lemonade. But what if we expect some drink but don't receive one? That case would be just as bad, s...
abort and unwind The previous section illustrates the error handling mechanism panic. Different code paths can be conditionally compiled based on the panic setting. The current values available are unwind and abort. Building on the prior le...
panic The simplest error handling mechanism we will see is panic. It prints an error message, starts unwinding the stack, and usually exits the program. Here, we explicitly call panic on our error condition: fn drink(beverage: &str) { // Yo...
Error handling Error handling is the process of handling the possibility of failure. For example, failing to read a file and then continuing to use that bad input would clearly be problematic. Noticing and explicitly managing those errors s...
Variadic Interfaces A variadic interface takes an arbitrary number of arguments. For example, println! can take an arbitrary number of arguments, as determined by the format string. We can extend our calculate! macro from the previous secti...
Domain Specific Languages (DSLs) A DSL is a mini "language" embedded in a Rust macro. It is completely valid Rust because the macro system expands into normal Rust constructs, but it looks like a small language. This allows you to define co...
DRY (Don't Repeat Yourself) Macros allow writing DRY code by factoring out the common parts of functions and/or test suites. Here is an example that implements and tests the +=, *= and -= operators on Vec<T>: use std::ops::{Add, Mul, Sub};...
Repeat Macros can use + in the argument list to indicate that an argument may repeat at least once, or *, to indicate that the argument may repeat zero or more times. In the following example, surrounding the matcher with $(...),+ will matc...
Overload Macros can be overloaded to accept different combinations of arguments. In that regard, macro_rules! can work similarly to a match block: // `test!` will compare `$left` and `$right` // in different ways depending on how you invoke...
Designators The arguments of a macro are prefixed by a dollar sign $ and type annotated with a designator: macro_rules! create_function { // This macro takes an argument of designator `ident` and // creates a function named `$func_name`. //...
Syntax In following subsections, we will show how to define macros in Rust. There are three basic ideas: • Patterns and Designators • Overloading • Repetition
macro_rules! Rust provides a powerful macro system that allows metaprogramming. As you've seen in previous chapters, macros look like functions, except that their name ends with a bang !, but instead of generating a function call, macros ar...
Disambiguating overlapping traits A type can implement many different traits. What if two traits both require the same name for a function? For example, many traits might have a method named get(). They might even have different return type...
Supertraits Rust doesn't have "inheritance", but you can define a trait as being a superset of another trait. For example: trait Person { fn name(&self) -> String; } // Person is a supertrait of Student. // Implementing Student requires you...