Rust by Example
Rust by Example
Rust by Example
Rust is a modern systems programming language focusing on safety, speed, and
concurrency. It accomplishes these goals by being memory safe without using garbage
collection.
Rust by Example (RBE) is a collection of runnable examples that illustrate various Rust
concepts and standard libraries. To get even more out of these examples, don't forget to
install Rust locally and check out the o cial docs. Additionally for the curious, you can also
check out the source code for this site.
Primitives - Learn about signed integers, unsigned integers and other primitives.
Conversion
Expressions
Generics - Learn about writing a function or data type which can work for multiple
types of arguments.
Scoping rules - Scopes play an important part in ownership, borrowing, and lifetimes.
Macros
Std library types - Learn about some custom types provided by std library.
Unsafe Operations
Hello World
This is the source code of the traditional Hello World program.
$ rustc hello.rs
$ ./hello
Hello World!
Activity
Click 'Run' above to see the expected output. Next, add a new line with a second println!
macro so that the output shows:
Hello World!
I'm a Rustacean!
Comments
Any program requires comments and indeed Rust supports a few di erent varieties:
https://rustbyexample.com/print.html 2/167
2018/1/7 Rust By Example
fn main() {
// This is an example of a line comment
// Notice how there are two slashes at the beginning of the line
// And that nothing written inside these will be read by the compiler
// println!("Hello, world!");
// Run it. See? Now try deleting the two slashes, and run it again.
/*
* This is another type of comment, the block comment. In general,
* the line comment is the recommended comment style however the
* block comment is extremely useful for temporarily disabling
* a large chunk of code. /* Block comments can be /* nested, */ */
* so it takes only a few keystrokes to comment out all the lines
* in this main() function. /*/*/* Try it yourself! */*/*/
*/
/*
Note, the previous column of `*` was entirely for style. There's
no actual need for it.
*/
See also:
Library documentation
Formatted print
Printing is handled by a series of macros de ned in std::fmt some of which include:
All parse text in the same fashion. A plus is that the formatting correctness will be checked
at compile time.
https://rustbyexample.com/print.html 3/167
2018/1/7 Rust By Example
fn main() {
// In general, the `{}` will be automatically replaced with any
// arguments. These will be stringified.
println!("{} days", 31);
// Without a suffix, 31 becomes an i32. You can change what type 31 is,
// with a suffix.
// You can right-align text with a specified width. This will output
// " 1". 5 white spaces and a "1".
println!("{number:>width$}", number=1, width=6);
// You can pad numbers with extra zeroes. This will output "000001".
println!("{number:>0width$}", number=1, width=6);
// It will even check to make sure the correct number of arguments are
// used.
println!("My name is {0}, {1} {0}", "Bond");
// FIXME ^ Add the missing argument: "James"
std::fmt contains many traits which govern the display of text. The base form of two
important ones are listed below:
fmt::Debug : Uses the {:?} marker. Format text for debugging purposes.
fmt::Display : Uses the {} marker. Format text in a more elegant, user friendly
fashion.
Here, fmt::Display was used because the std library provides implementations for these
types. To print text for custom types, more steps are required.
Activities
Fix the two issues in the above code (see FIXME) so that it runs without error.
Add a println! macro that prints: Pi is roughly 3.142 by controlling the number
of decimal places shown. For the purposes of this exercise, use let pi = 3.141592 as
an estimate for Pi. (Hint: you may need to check the std::fmt documentation for
setting the number of decimals to display)
https://rustbyexample.com/print.html 4/167
2018/1/7 Rust By Example
See also
Debug
All types which want to use std::fmt formatting traits require an implementation to be
printable. Automatic implementations are only provided for types such as in the std
library. All others must be manually implemented somehow.
The fmt::Debug trait makes this very straightforward. All types can derive
(automatically create) the fmt::Debug implementation. This is not true for fmt::Display
which must be manually implemented.
// This structure cannot be printed either with `fmt::Display` or
// with `fmt::Debug`
struct UnPrintable(i32);
All std library types automatically are printable with {:?} too:
fn main() {
// Printing with `{:?}` is similar to with `{}`.
println!("{:?} months in a year.", 12);
println!("{1:?} {0:?} is the {actor:?} name.",
"Slater",
"Christian",
actor="actor's");
// `Structure` is printable!
println!("Now {:?} will print!", Structure(3));
So fmt::Debug de nitely makes this printable but sacri ces some elegance. Rust also
provides "pretty printing" with {:#?} .
https://rustbyexample.com/print.html 5/167
2018/1/7 Rust By Example
#[derive(Debug)]
struct Person<'a> {
name: &'a str,
age: u8
}
fn main() {
let name = "Peter";
let age = 27;
let peter = Person { name, age };
// Pretty print
println!("{:#?}", peter);
}
See also
Display
fmt::Debug hardly looks compact and clean, so it is often advantageous to customize the
output appearance. This is done by manually implementing fmt::Display , which uses the
{} print marker. Implementing it looks like this:
// Import (via `use`) the `fmt` module to make it available.
use std::fmt;
// In order to use the `{}` marker, the trait `fmt::Display` must be implemented
// manually for the type.
impl fmt::Display for Structure {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Write strictly the first element into the supplied output
// stream: `f`. Returns `fmt::Result` which indicates whether the
// operation succeeded or failed. Note that `write!` uses syntax which
// is very similar to `println!`.
write!(f, "{}", self.0)
}
}
fmt::Display may be cleaner than fmt::Debug but this presents a problem for the std
library. How should ambiguous types be displayed? For example, if the std library
implemented a single style for all Vec<T> , what style should it be? Either of these two?
https://rustbyexample.com/print.html 6/167
2018/1/7 Rust By Example
No, because there is no ideal style for all types and the std library doesn't presume to
dictate one. fmt::Display is not implemented for Vec<T> or for any other generic
containers. fmt::Debug must then be used for these generic cases.
This is not a problem though because for any new container type which is not generic,
fmt::Display can be implemented.
fn main() {
let minmax = MinMax(0, 14);
println!("Compare structures:");
println!("Display: {}", minmax);
println!("Debug: {:?}", minmax);
println!("Compare points:");
println!("Display: {}", point);
println!("Debug: {:?}", point);
https://rustbyexample.com/print.html 7/167
2018/1/7 Rust By Example
So, fmt::Display has been implemented but fmt::Binary has not, and therefore cannot
be used. std::fmt has many such traits and each requires its own implementation. This
is detailed further in std::fmt .
Activity
After checking the output of the above example, use the Point2D struct as guide to add a
Complex struct to the example. When printed in the same way, the output should be:
See also
Testcase: List
Implementing fmt::Display for a structure where the elements must each be handled
sequentially is tricky. The problem is that each write! generates a fmt::Result . Proper
handling of this requires dealing with all the results. Rust provides the ? operator for
exactly this purpose.
Alternatively, you can also use the try! macro, which works the same way. This is a bit
more verbose and no longer recommended, but you may still see it in older Rust code.
Using try! looks like this:
https://rustbyexample.com/print.html 8/167
2018/1/7 Rust By Example
use std::fmt; // Import the `fmt` module.
// Define a structure named `List` containing a `Vec`.
struct List(Vec<i32>);
write!(f, "[")?;
fn main() {
let v = List(vec![1, 2, 3]);
println!("{}", v);
}
Activity
Try changing the program so that the index of each element in the vector is also printed.
The new output should look like this:
[0: 1, 1: 2, 2: 3]
See also
Formatting
We've seen that formatting is speci ed via a format string:
The same variable ( foo ) can be formatted di erently depending on which argument type is
used: X vs o vs unspeci ed.
https://rustbyexample.com/print.html 9/167
2018/1/7 Rust By Example
This formatting functionality is implemented via traits, and there is one trait for each
argument type. The most common formatting trait is Display , which handles cases where
the argument type is left unspeci ed: {} for instance.
#[derive(Debug)]
struct Color {
red: u8,
green: u8,
blue: u8,
}
fn main() {
for city in [
City { name: "Dublin", lat: 53.347778, lon: -6.259722 },
City { name: "Oslo", lat: 59.95, lon: 10.75 },
City { name: "Vancouver", lat: 49.25, lon: -123.1 },
].iter() {
println!("{}", *city);
}
for color in [
Color { red: 128, green: 255, blue: 90 },
Color { red: 0, green: 3, blue: 254 },
Color { red: 0, green: 0, blue: 0 },
].iter() {
// Switch this to use {} once you've added an implementation
// for fmt::Display
println!("{:?}", *color);
}
}
You can view a full list of formatting traits and their argument types in the std::fmt
documentation.
Activity
Add an implementation of the fmt::Display trait for the Color struct above so that the
output displays as:
https://rustbyexample.com/print.html 10/167
2018/1/7 Rust By Example
See also
std::fmt
Primitives
Rust provides access to a wide variety of primitives . A sample includes:
Scalar Types
Despite the value of a unit type being a tuple, it is not considered a compound type because
it does not contain multiple values.
Compound Types
Variables can always be type annotated. Numbers may additionally be annotated via a su x
or by default. Integers default to i32 and oats to f64 . Note that Rust can also infer types
from context.
https://rustbyexample.com/print.html 11/167
2018/1/7 Rust By Example
fn main() {
// Variables can be type annotated.
let logical: bool = true;
See also:
Integers can, alternatively, be expressed using hexadecimal, octal or binary notation using
either of these pre xes: 0x , 0o or 0b .
Underscores can be inserted in numeric literals to improve readability, e.g. 1_000 is the
same as 1000 , and 0.000_001 is the same as 0.000001 .
We need to tell the compiler the type of the literals we use. For now, we'll use the u32 su x
to indicate that the literal is an unsigned 32-bit integer, and the i32 su x to indicate that
it's a signed 32-bit integer.
The operators available and their precedence in Rust are similar to other C-like languages.
https://rustbyexample.com/print.html 12/167
2018/1/7 Rust By Example
fn main() {
// Integer addition
println!("1 + 2 = {}", 1u32 + 2);
// Integer subtraction
println!("1 - 2 = {}", 1i32 - 2);
// TODO ^ Try changing `1i32` to `1u32` to see why the type is important
// Bitwise operations
println!("0011 AND 0101 is {:04b}", 0b0011u32 & 0b0101);
println!("0011 OR 0101 is {:04b}", 0b0011u32 | 0b0101);
println!("0011 XOR 0101 is {:04b}", 0b0011u32 ^ 0b0101);
println!("1 << 5 is {}", 1u32 << 5);
println!("0x80 >> 2 is 0x{:x}", 0x80u32 >> 2);
Tuples
A tuple is a collection of values of di erent types. Tuples are constructed using parentheses
() , and each tuple itself is a value with type signature (T1, T2, ...) , where T1 , T2 are
the types of its members. Functions can use tuples to return multiple values, as tuples can
hold any number of values.
https://rustbyexample.com/print.html 13/167
2018/1/7 Rust By Example
// Tuples can be used as function arguments and as return values
fn reverse(pair: (i32, bool)) -> (bool, i32) {
// `let` can be used to bind the members of a tuple to variables
let (integer, boolean) = pair;
(boolean, integer)
}
fn main() {
// A tuple with a bunch of different types
let long_tuple = (1u8, 2u16, 3u32, 4u64,
-1i8, -2i16, -3i32, -4i64,
0.1f32, 0.2f64,
'a', true);
// To create one element tuples, the comma is required to tell them apart
// from a literal surrounded by parentheses
println!("one element tuple: {:?}", (5u32,));
println!("just an integer: {:?}", (5u32));
Activity
1. Recap: Add the fmt::Display trait to the Matrix struct in the above example, so that
if you switch from printing the debug format {:?} to the display format {} , you see
the following output:
( 1.1 1.2 )
( 2.1 2.2 )
https://rustbyexample.com/print.html 14/167
2018/1/7 Rust By Example
You may want to refer back to the example for print display.
2. Add a transpose function using the reverse function as a template, which accepts a
matrix as an argument, and returns a matrix in which two elements have been
swapped. For example:
println!("Matrix:\n{}", matrix);
println!("Transpose:\n{}", transpose(matrix));
Matrix:
( 1.1 1.2 )
( 2.1 2.2 )
Transpose:
( 1.1 2.1 )
( 1.2 2.2 )
Slices are similar to arrays, but their size is not known at compile time. Instead, a slice is a
two-word object, the rst word is a pointer to the data, and the second word is the length of
the slice. The word size is the same as usize, determined by the processor architecture eg 64
bits on an x86-64. Slices can be used to borrow a section of an array, and have the type
signature &[T] .
https://rustbyexample.com/print.html 15/167
2018/1/7 Rust By Example
use std::mem;
// This function borrows a slice
fn analyze_slice(slice: &[i32]) {
println!("first element of the slice: {}", slice[0]);
println!("the slice has {} elements", slice.len());
}
fn main() {
// Fixed-size array (type signature is superfluous)
let xs: [i32; 5] = [1, 2, 3, 4, 5];
// Indexing starts at 0
println!("first element of the array: {}", xs[0]);
println!("second element of the array: {}", xs[1]);
Custom Types
Rust custom data types are formed mainly through the two keywords:
struct : de ne a structure
enum : de ne an enumeration
Constants can also be created via the const and static keywords.
Structures
There are three types of structures ("structs") that can be created using the struct
keyword:
https://rustbyexample.com/print.html 16/167
2018/1/7 Rust By Example
#[derive(Debug)]
struct Person<'a> {
name: &'a str,
age: u8,
}
// A unit struct
struct Nil;
// A tuple struct
struct Pair(i32, f32);
fn main() {
// Create struct with field init shorthand
let name = "Peter";
let age = 27;
let peter = Person { name, age };
// Instantiate a `Point`
let point: Point = Point { x: 0.3, y: 0.4 };
Activity
https://rustbyexample.com/print.html 17/167
2018/1/7 Rust By Example
1. Add a function rect_area which calculates the area of a rectangle (try using nested
destructuring).
2. Add a function square which takes a Point and a f32 as arguments, and returns a
Rectangle with its lower left corner on the point, and a width and height
corresponding to the f32 .
See also:
Enums
The enum keyword allows the creation of a type which may be one of a few di erent
variants. Any variant which is valid as a struct is also valid as an enum .
https://rustbyexample.com/print.html 18/167
2018/1/7 Rust By Example
// An attribute to hide warnings for unused code.
#![allow(dead_code)]
fn main() {
let pressed = WebEvent::KeyPress('x');
// `to_owned()` creates an owned `String` from a string slice.
let pasted = WebEvent::Paste("my text".to_owned());
let click = WebEvent::Click { x: 20, y: 80 };
let load = WebEvent::PageLoad;
let unload = WebEvent::PageUnload;
inspect(pressed);
inspect(pasted);
inspect(click);
inspect(load);
inspect(unload);
}
See also:
use
The use declaration can be used so manual scoping isn't needed:
https://rustbyexample.com/print.html 19/167
2018/1/7 Rust By Example
// An attribute to hide warnings for unused code.
#![allow(dead_code)]
enum Status {
Rich,
Poor,
}
enum Work {
Civilian,
Soldier,
}
fn main() {
// Explicitly `use` each name so they are available without
// manual scoping.
use Status::{Poor, Rich};
// Automatically `use` each name inside `Work`.
use Work::*;
// Equivalent to `Status::Poor`.
let status = Poor;
// Equivalent to `Work::Civilian`.
let work = Civilian;
match status {
// Note the lack of scoping because of the explicit `use` above.
Rich => println!("The rich have lots of money!"),
Poor => println!("The poor have no money..."),
}
match work {
// Note again the lack of scoping.
Civilian => println!("Civilians work!"),
Soldier => println!("Soldiers fight!"),
}
}
See also:
C-like
enum can also be used as C-like enums.
https://rustbyexample.com/print.html 20/167
2018/1/7 Rust By Example
// An attribute to hide warnings for unused code.
#![allow(dead_code)]
fn main() {
// `enums` can be cast as integers.
println!("zero is {}", Number::Zero as i32);
println!("one is {}", Number::One as i32);
See also:
casting
Testcase: linked-list
A common use for enums is to create a linked-list:
https://rustbyexample.com/print.html 21/167
2018/1/7 Rust By Example
use List::*;
enum List {
// Cons: Tuple struct that wraps an element and a pointer to the next node
Cons(u32, Box<List>),
// Nil: A node that signifies the end of the linked list
Nil,
}
// Consume a list, and return the same list with a new element at its front
fn prepend(self, elem: u32) -> List {
// `Cons` also has type List
Cons(elem, Box::new(self))
}
fn main() {
// Create an empty linked list
let mut list = List::new();
https://rustbyexample.com/print.html 22/167
2018/1/7 Rust By Example
See also:
constants
Rust has two di erent types of constants which can be declared in any scope including
global. Both require explicit type annotation:
One special case is the "string" literal. It can be assigned directly to a static variable
without modi cation because its type signature: &'static str has the required lifetime of
'static . All other reference types must be speci cally annotated so that they ful ll the
'static lifetime. This may seem minor though because the required explicit annotation
hides the distinction.
fn main() {
let n = 16;
See also:
Variable Bindings
Rust provides type safety via static typing. Variable bindings can be type annotated when
declared. However, in most cases, the compiler will be able to infer the type of the variable
from the context, heavily reducing the annotation burden.
Values (like literals) can be bound to variables, using the let binding.
https://rustbyexample.com/print.html 23/167
2018/1/7 Rust By Example
fn main() {
let an_integer = 1u32;
let a_boolean = true;
let unit = ();
// The compiler warns about unused variable bindings; these warnings can
// be silenced by prefixing the variable name with an underscore
let _unused_variable = 3u32;
Mutability
Variable bindings are immutable by default, but this can be overridden using the mut
modi er.
fn main() {
let _immutable_binding = 1;
let mut mutable_binding = 1;
// Ok
mutable_binding += 1;
// Error!
_immutable_binding += 1;
// FIXME ^ Comment out this line
}
https://rustbyexample.com/print.html 24/167
2018/1/7 Rust By Example
fn main() {
// This binding lives in the main function
let long_lived_binding = 1;
// This is a block, and has a smaller scope than the main function
{
// This binding only exists in this block
let short_lived_binding = 2;
Declare rst
It's possible to declare variable bindings rst, and initialize them later. However, this form is
seldom used, as it may lead to the use of uninitialized variables.
fn main() {
// Declare a variable binding
let a_binding;
{
let x = 2;
let another_binding;
another_binding = 1;
The compiler forbids use of uninitialized variables, as this would lead to unde ned behavior.
https://rustbyexample.com/print.html 25/167
2018/1/7 Rust By Example
Types
Rust provides several mechanisms to change or de ne the type of primitive and user
de ned types. The following sections cover:
Casting
Rust provides no implicit type conversion (coercion) between primitive types. But, explicit
type conversion (casting) can be performed using the as keyword.
Rules for converting between integral types follow C conventions generally, except in cases
where C has unde ned behavior. The behavior of all casts between integral types is well
de ned in Rust.
https://rustbyexample.com/print.html 26/167
2018/1/7 Rust By Example
// Suppress all warnings from casts which overflow.
#![allow(overflowing_literals)]
fn main() {
let decimal = 65.4321_f32;
// Explicit conversion
let integer = decimal as u8;
let character = integer as char;
Literals
Numeric literals can be type annotated by adding the type as a su x. As an example, to
specify that the literal 42 should have the type i32 , write 42i32 .
The type of unsu xed numeric literals will depend on how they are used. If no constraint
exists, the compiler will use i32 for integers, and f64 for oating-point numbers.
https://rustbyexample.com/print.html 27/167
2018/1/7 Rust By Example
fn main() {
// Suffixed literals, their types are known at initialization
let x = 1u8;
let y = 2u32;
let z = 3f32;
There are some concepts used in the previous code that haven't been explained yet, here's
a brief explanation for the impatient readers:
Inference
The type inference engine is pretty smart. It does more than looking at the type of the r-
value during an initialization. It also looks at how the variable is used afterwards to infer its
type. Here's an advanced example of type inference:
fn main() {
// Because of the annotation, the compiler knows that `elem` has type u8.
let elem = 5u8;
println!("{:?}", vec);
}
No type annotation of variables was needed, the compiler is happy and so is the
programmer!
Aliasing
https://rustbyexample.com/print.html 28/167
2018/1/7 Rust By Example
The type statement can be used to give a new name to an existing type. Types must have
CamelCase names, or the compiler will raise a warning. The exception to this rule are the
primitive types: usize , f32 , etc.
fn main() {
// `NanoSecond` = `Inch` = `u64_t` = `u64`.
let nanoseconds: NanoSecond = 5 as u64_t;
let inches: Inch = 2 as u64_t;
// Note that type aliases *don't* provide any extra type safety, because
// aliases are *not* new types
println!("{} nanoseconds + {} inches = {} unit?",
nanoseconds,
inches,
nanoseconds + inches);
}
The main use of aliases is to reduce boilerplate; for example the IoResult<T> type is an
alias for the Result<T, IoError> type.
See also:
Attributes
Conversion
Rust addresses conversion between types by the use of traits. The generic conversions will
use the From and Into traits. However there are more speci c ones for the more common
cases, in particular when converting to and from String s.
From
https://rustbyexample.com/print.html 29/167
2018/1/7 Rust By Example
The From trait allows for a type to de ne how to create itself from another type, hence
providing a very simple mechanism for converting between several types. There are
numerous implementations of this trait within the standard library for conversion of
primitive and common types.
let my_str = "hello";
let my_string = String::from(my_str);
use std::convert::From;
#[derive(Debug)]
struct Number {
value: i32,
}
fn main() {
let num = Number::from(30);
println!("My number is {:?}", num);
}
Into
The Into trait is simply the reciprocal of the From trait. That is, if you have implemented
the From trait for your type you get the Into implementation for free.
Using the Into trait will typically require speci cation of the type to convert into as the
compiler is unable to determine this most of the time. However this is a small trade o
considering we get the functionality for free.
https://rustbyexample.com/print.html 30/167
2018/1/7 Rust By Example
use std::convert::From;
#[derive(Debug)]
struct Number {
value: i32,
}
fn main() {
let int = 5;
// Try removing the type declaration
let num: Number = int.into();
println!("My number is {:?}", num);
}
ToString
To convert any type to a String it is as simple as implementing the ToString trait for the
type.
use std::string::ToString;
struct Circle {
radius: i32
}
fn main() {
let circle = Circle { radius: 6 };
println!("{}", circle.to_string());
}
Parsing a String
One of the more common types to convert a string into is a number. The idiomatic
approach to this is to use the parse function and provide the type for the function to parse
the string value into, this can be done either without type inference or using the 'turbo sh'
syntax.
https://rustbyexample.com/print.html 31/167
2018/1/7 Rust By Example
This will convert the string into the type speci ed so long as the FromStr trait is
implemented for that type. This is implemented for numerous types within the standard
library. To obtain this functionality on a user de ned type simply implement the FromStr
trait for that type.
fn main() {
let parsed: i32 = "5".parse().unwrap();
let turbo_parsed = "10".parse::<i32>().unwrap();
Expressions
A Rust program is (mostly) made up of a series of statements:
fn main() {
// statement
// statement
// statement
}
There are a few kinds of statements in Rust. The most common two are declaring a variable
binding, and using a ; with an expression:
fn main() {
// variable binding
let x = 5;
// expression;
x;
x + 1;
15;
}
Blocks are expressions too, so they can be used as r-values in assignments. The last
expression in the block will be assigned to the l-value. However, if the last expression of the
block ends with a semicolon, the return value will be () .
https://rustbyexample.com/print.html 32/167
2018/1/7 Rust By Example
fn main() {
let x = 5u32;
let y = {
let x_squared = x * x;
let x_cube = x_squared * x;
let z = {
// The semicolon suppresses this expression and `()` is assigned to `z`
2 * x;
};
Flow Control
An essential part of any programming languages are ways to modify control ow: if / else ,
for , and others. Let's talk about them in Rust.
if/else
Branching with if - else is similar to other languages. Unlike many of them, the boolean
condition doesn't need to be surrounded by parentheses, and each condition is followed by
a block. if - else conditionals are expressions, and, all branches must return the same
type.
https://rustbyexample.com/print.html 33/167
2018/1/7 Rust By Example
fn main() {
let n = 5;
if n < 0 {
print!("{} is negative", n);
} else if n > 0 {
print!("{} is positive", n);
} else {
print!("{} is zero", n);
}
let big_n =
if n < 10 && n > -10 {
println!(", and is a small number, increase ten-fold");
loop
Rust provides a loop keyword to indicate an in nite loop.
The break statement can be used to exit a loop at anytime, whereas the continue
statement can be used to skip the rest of the iteration and start a new one.
fn main() {
let mut count = 0u32;
// Infinite loop
loop {
count += 1;
if count == 3 {
println!("three");
println!("{}", count);
if count == 5 {
println!("OK, that's enough");
https://rustbyexample.com/print.html 34/167
2018/1/7 Rust By Example
#![allow(unreachable_code)]
fn main() {
'outer: loop {
println!("Entered the outer loop");
'inner: loop {
println!("Entered the inner loop");
fn main() {
let mut counter = 0;
if counter == 10 {
break counter * 2;
}
};
assert_eq!(result, 20);
}
while
The while keyword can be used to loop until a condition is met.
https://rustbyexample.com/print.html 35/167
2018/1/7 Rust By Example
fn main() {
// A counter variable
let mut n = 1;
// Increment counter
n += 1;
}
}
for loops
fn main() {
// `n` will take the values: 1, 2, ..., 100 in each iteration
for n in 1..101 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 3 == 0 {
println!("fizz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
}
}
https://rustbyexample.com/print.html 36/167
2018/1/7 Rust By Example
to convert a collection into an iterator however, the other functions available include iter
and iter_mut .
These 3 functions will return di erent views of the data within your collection.
iter - This borrows each element of the collection through each iteration. Thus
leaving the collection untouched and available for reuse after the loop.
into_iter - This consumes the collection so that on each iteration the exact data is
provided. Once the collection has been consumed it is no longer available for reuse as
it has been 'moved' within the loop.
iter_mut - This mutably borrows each element of the collection, allowing for the
collection to be modi ed in place.
In the above snippets note the type of match branch, that is the key di erence in the types
or iteration. The di erence in type then of course implies di ering actions that are able to
be performed.
See also
Iterator
match
https://rustbyexample.com/print.html 37/167
2018/1/7 Rust By Example
Rust provides pattern matching via the match keyword, which can be used like a C switch .
fn main() {
let number = 13;
// TODO ^ Try different values for `number`
Destructuring
A match block can destructure items in a variety of ways.
Destructuring Enums
Destructuring Pointers
Destructuring Structures
Destructuring Tuples
tuples
Tuples can be destructured in a match as follows:
fn main() {
let pair = (0, -2);
// TODO ^ Try different values for `pair`
https://rustbyexample.com/print.html 38/167
2018/1/7 Rust By Example
See also:
Tuples
enums
An enum is destructured similarly:
fn main() {
let color = Color::RGB(122, 17, 40);
// TODO ^ Try different variants for `color`
See also:
pointers/ref
For pointers, a distinction needs to be made between destructuring and dereferencing as
they are di erent concepts which are used di erently from a language like C .
https://rustbyexample.com/print.html 39/167
2018/1/7 Rust By Example
Dereferencing uses *
Destructuring uses & , ref , and ref mut
fn main() {
// Assign a reference of type `i32`. The `&` signifies there
// is a reference being assigned.
let reference = &4;
match reference {
// If `reference`s is pattern matched against `&val`, it results
// in a comparison like:
// `&i32`
// `&val`
// ^ We see that if the matching `&`s are dropped, then the `i32`
// should be assigned to `val`.
&val => println!("Got a value via destructuring: {:?}", val),
}
structs
Similarly, a struct can be destructured as shown:
https://rustbyexample.com/print.html 40/167
2018/1/7 Rust By Example
fn main() {
struct Foo { x: (u32, u32), y: u32 }
// this will give an error: pattern does not mention field `x`
// let Foo { y } = foo;
}
See also:
Guards
A match guard can be added to lter the arm.
fn main() {
let pair = (2, -2);
// TODO ^ Try different values for `pair`
See also:
Tuples
Binding
Indirectly accessing a variable makes it impossible to branch and use that variable without
re-binding. match provides the @ sigil for binding values to names:
https://rustbyexample.com/print.html 41/167
2018/1/7 Rust By Example
// A function `age` which returns a `u32`.
fn age() -> u32 {
15
}
fn main() {
println!("Tell me type of person you are");
match age() {
0 => println!("I'm not born yet I guess"),
// Could `match` 1 ... 12 directly but then what age
// would the child be? Instead, bind to `n` for the
// sequence of 1 .. 12. Now the age can be reported.
n @ 1 ... 12 => println!("I'm a child of age {:?}", n),
n @ 13 ... 19 => println!("I'm a teen of age {:?}", n),
// Nothing bound. Return the result.
n => println!("I'm an old person of age {:?}", n),
}
}
See also:
functions
if let
For some use cases, match is awkward. For example:
// Make `optional` of type `Option<i32>`
let optional = Some(7);
match optional {
Some(i) => {
println!("This is a really long string and `{:?}`", i);
// ^ Needed 2 indentations just so we could destructure
// `i` from the option.
},
_ => {},
// ^ Required because `match` is exhaustive. Doesn't it seem
// like wasted space?
};
if let is cleaner for this use case and in addition allows various failure options to be
speci ed:
https://rustbyexample.com/print.html 42/167
2018/1/7 Rust By Example
fn main() {
// All have type `Option<i32>`
let number = Some(7);
let letter: Option<i32> = None;
let emoticon: Option<i32> = None;
// The `if let` construct reads: "if `let` destructures `number` into
// `Some(i)`, evaluate the block (`{}`).
if let Some(i) = number {
println!("Matched {:?}!", i);
}
See also:
while let
Similar to if let , while let can make awkward match sequences more tolerable.
Consider the following sequence that increments i :
https://rustbyexample.com/print.html 43/167
2018/1/7 Rust By Example
// Make `optional` of type `Option<i32>`
let mut optional = Some(0);
fn main() {
// Make `optional` of type `Option<i32>`
let mut optional = Some(0);
See also:
Functions
Functions are declared using the fn keyword. Its arguments are type annotated, just like
variables, and, if the function returns a value, the return type must be speci ed after an
https://rustbyexample.com/print.html 44/167
2018/1/7 Rust By Example
arrow -> .
The nal expression in the function will be used as return value. Alternatively, the return
statement can be used to return a value earlier from within the function, even from inside
loops or if s.
// Functions that "don't" return a value, actually return the unit type `()`
fn fizzbuzz(n: u32) -> () {
if is_divisible_by(n, 15) {
println!("fizzbuzz");
} else if is_divisible_by(n, 3) {
println!("fizz");
} else if is_divisible_by(n, 5) {
println!("buzz");
} else {
println!("{}", n);
}
}
// When a function returns `()`, the return type can be omitted from the
// signature
fn fizzbuzz_to(n: u32) {
for n in 1..n + 1 {
fizzbuzz(n);
}
}
Methods
Methods are functions attached to objects. These methods have access to the data of the
object and its other methods via the self keyword. Methods are de ned under an impl
block.
https://rustbyexample.com/print.html 45/167
2018/1/7 Rust By Example
struct Point {
x: f64,
y: f64,
}
struct Rectangle {
p1: Point,
p2: Point,
}
impl Rectangle {
// This is an instance method
// `&self` is sugar for `self: &Self`, where `Self` is the type of the
// caller object. In this case `Self` = `Rectangle`
fn area(&self) -> f64 {
// `self` gives access to the struct fields via the dot operator
let Point { x: x1, y: y1 } = self.p1;
let Point { x: x2, y: y2 } = self.p2;
self.p1.y += y;
self.p2.y += y;
}
}
impl Pair {
// This method "consumes" the resources of the caller object
// `self` desugars to `self: Self`
fn destroy(self) {
// Destructure `self`
let Pair(first, second) = self;
https://rustbyexample.com/print.html 46/167
2018/1/7 Rust By Example
fn main() {
let rectangle = Rectangle {
// Static methods are called using double colons
p1: Point::origin(),
p2: Point::new(3.0, 4.0),
};
pair.destroy();
Closures
Closures in Rust, also called lambdas, are functions that can capture the enclosing
environment. For example, a closure that captures the x variable:
|val| val + x
The syntax and capabilities of closures make them very convenient for on the y usage.
Calling a closure is exactly like calling a function. However, both input and return types can
be inferred and input variable names must be speci ed.
https://rustbyexample.com/print.html 47/167
2018/1/7 Rust By Example
fn main() {
// Increment via closures and functions.
fn function (i: i32) -> i32 { i + 1 }
let i = 1;
// Call the function and closures.
println!("function: {}", function(i));
println!("closure_annotated: {}", closure_annotated(i));
println!("closure_inferred: {}", closure_inferred(i));
Capturing
Closures are inherently exible and will do what the functionality requires to make the
closure work without annotation. This allows capturing to exibly adapt to the use case,
sometimes moving and sometimes borrowing. Closures can capture variables:
by reference: &T
by mutable reference: &mut T
by value: T
They preferentially capture variables by reference and only go lower when required.
https://rustbyexample.com/print.html 48/167
2018/1/7 Rust By Example
fn main() {
use std::mem;
// A non-copy type.
let movable = Box::new(3);
See also:
As input parameters
https://rustbyexample.com/print.html 49/167
2018/1/7 Rust By Example
While Rust chooses how to capture variables on the y mostly without type annotation, this
ambiguity is not allowed when writing functions. When taking a closure as an input
parameter, the closure's complete type must be annotated using one of a few traits . In
order of decreasing restriction, they are:
On a variable-by-variable basis, the compiler will capture variables in the least restrictive
manner possible.
For instance, consider a parameter annotated as FnOnce . This speci es that the closure
may capture by &T , &mut T , or T , but the compiler will ultimately choose based on how
the captured variables are used in the closure.
This is because if a move is possible, then any type of borrow should also be possible. Note
that the reverse is not true. If the parameter is annotated as Fn , then capturing variables by
&mut T or T are not allowed.
In the following example, try swapping the usage of Fn , FnMut , and FnOnce to see what
happens:
https://rustbyexample.com/print.html 50/167
2018/1/7 Rust By Example
// A function which takes a closure as an argument and calls it.
fn apply<F>(f: F) where
// The closure takes no input and returns nothing.
F: FnOnce() {
// ^ TODO: Try changing this to `Fn` or `FnMut`.
f();
}
f(3)
}
fn main() {
use std::mem;
See also:
Type anonymity
Closures succinctly capture variables from enclosing scopes. Does this have any
consequences? It surely does. Observe how using a closure as a function parameter
requires generics, which is necessary because of how they are de ned:
https://rustbyexample.com/print.html 51/167
2018/1/7 Rust By Example
// `F` must be generic.
fn apply<F>(f: F) where
F: FnOnce() {
f();
}
When a closure is de ned, the compiler implicitly creates a new anonymous structure to
store the captured variables inside, meanwhile implementing the functionality via one of the
traits : Fn , FnMut , or FnOnce for this unknown type. This type is assigned to the variable
which is stored until calling.
Since this new type is of unknown type, any usage in a function will require generics.
However, an unbounded type parameter <T> would still be ambiguous and not be allowed.
Thus, bounding by one of the traits : Fn , FnMut , or FnOnce (which it implements) is
su cient to specify its type.
fn main() {
let x = 7;
apply(print);
}
See also:
Input functions
Since closures may be used as arguments, you might wonder if the same can be said about
functions. And indeed they can! If you declare a function that takes a closure as parameter,
then any function that satis es the trait bound of that closure can be passed as a
parameter.
https://rustbyexample.com/print.html 52/167
2018/1/7 Rust By Example
// Define a function which takes a generic `F` argument
// bounded by `Fn`, and calls it
fn call_me<F: Fn()>(f: F) {
f();
}
fn main() {
// Define a closure satisfying the `Fn` bound
let closure = || println!("I'm a closure!");
call_me(closure);
call_me(function);
}
As an additional note, the Fn , FnMut , and FnOnce traits dictate how a closure captures
variables from the enclosing scope.
See also:
As output parameters
Closures as input parameters are possible, so returning closures as output parameters
should also be possible. However, returning closure types are problematic because Rust
currently only supports returning concrete (non-generic) types. Anonymous closure types
are, by de nition, unknown and so returning a closure is only possible by making it
concrete. This can be done via boxing.
The valid traits for returns are slightly di erent than before:
Fn : normal
FnMut : normal
FnOnce : There are some unusual things at play here, so the FnBox type is currently
needed, and is unstable. This is expected to change in the future.
Beyond this, the move keyword must be used, which signals that all captures occur by value.
This is required because any captures by reference would be dropped as soon as the
function exited, leaving invalid references in the closure.
https://rustbyexample.com/print.html 53/167
2018/1/7 Rust By Example
fn create_fn() -> Box<Fn()> {
let text = "Fn".to_owned();
fn main() {
let fn_plain = create_fn();
let mut fn_mut = create_fnmut();
fn_plain();
fn_mut();
}
See also:
Examples in std
This section contains a few examples of using closures from the std library.
Iterator::any
Iterator::any is a function which when passed an iterator, will return true if any element
satis es the predicate. Otherwise false . Its signature:
https://rustbyexample.com/print.html 54/167
2018/1/7 Rust By Example
fn main() {
let vec1 = vec![1, 2, 3];
let vec2 = vec![4, 5, 6];
See also:
std::iter::Iterator::any
Iterator:: nd
Iterator::find is a function which when passed an iterator, will return the rst element
which satis es the predicate as an Option . Its signature:
https://rustbyexample.com/print.html 55/167
2018/1/7 Rust By Example
fn main() {
let vec1 = vec![1, 2, 3];
let vec2 = vec![4, 5, 6];
See also:
std::iter::Iterator::find
https://rustbyexample.com/print.html 56/167
2018/1/7 Rust By Example
fn is_odd(n: u32) -> bool {
n % 2 == 1
}
fn main() {
println!("Find the sum of all the squared odd numbers under 1000");
let upper = 1000;
// Imperative approach
// Declare accumulator variable
let mut acc = 0;
// Iterate: 0, 1, 2, ... to infinity
for n in 0.. {
// Square the number
let n_squared = n * n;
// Functional approach
let sum_of_squared_odd_numbers: u32 =
(0..).map(|n| n * n) // All natural numbers squared
.take_while(|&n_squared| n_squared < upper) // Below upper limit
.filter(|&n_squared| is_odd(n_squared)) // That are odd
.fold(0, |acc, n_squared| acc + n_squared); // Sum them
println!("functional style: {}", sum_of_squared_odd_numbers);
}
Modules
Rust provides a powerful module system that can be used to hierarchically split code in
logical units (modules), and manage visibility (public/private) between them.
A module is a collection of items: functions, structs, traits, impl blocks, and even other
modules.
Visibility
By default, the items in a module have private visibility, but this can be overridden with the
pub modi er. Only the public items of a module can be accessed from outside the module
scope.
https://rustbyexample.com/print.html 57/167
2018/1/7 Rust By Example
// A module named `my_mod`
mod my_mod {
// Items in modules default to private visibility.
fn private_function() {
println!("called `my_mod::private_function()`");
}
#[allow(dead_code)]
fn private_function() {
println!("called `my_mod::nested::private_function()`");
}
pub fn call_public_function_in_my_mod() {
print!("called `my_mod::call_public_funcion_in_my_mod()`, that\n> ");
nested::public_function_in_my_mod();
print!("> ");
nested::public_function_in_super_mod();
}
fn function() {
https://rustbyexample.com/print.html 58/167
2018/1/7 Rust By Example
println!("called `function()`");
}
fn main() {
// Modules allow disambiguation between items that have the same name.
function();
my_mod::function();
// pub(in path) items can only be called from within the mode specified
// Error! function `public_function_in_my_mod` is private
//my_mod::nested::public_function_in_my_mod();
// TODO ^ Try uncommenting this line
Struct visibility
Structs have an extra level of visibility with their elds. The visibility defaults to private, and
can be overridden with the pub modi er. This visibility only matters when a struct is
accessed from outside the module where it is de ned, and has the goal of hiding
information (encapsulation).
https://rustbyexample.com/print.html 59/167
2018/1/7 Rust By Example
mod my {
// A public struct with a public field of generic type `T`
pub struct OpenBox<T> {
pub contents: T,
}
impl<T> ClosedBox<T> {
// A public constructor method
pub fn new(contents: T) -> ClosedBox<T> {
ClosedBox {
contents: contents,
}
}
}
}
fn main() {
// Public structs with public fields can be constructed as usual
let open_box = my::OpenBox { contents: "public information" };
// Public structs with private fields cannot be constructed using field names.
// Error! `ClosedBox` has private fields
//let closed_box = my::ClosedBox { contents: "classified information" };
// TODO ^ Try uncommenting this line
See also:
https://rustbyexample.com/print.html 60/167
2018/1/7 Rust By Example
// Bind the `deeply::nested::function` path to `other_function`.
use deeply::nested::function as other_function;
fn function() {
println!("called `function()`");
}
mod deeply {
pub mod nested {
pub fn function() {
println!("called `deeply::nested::function()`");
}
}
}
fn main() {
// Easier access to `deeply::nested::function`
other_function();
println!("Entering block");
{
// This is equivalent to `use deeply::nested::function as function`.
// This `function()` will shadow the outer one.
use deeply::nested::function;
function();
function();
}
https://rustbyexample.com/print.html 61/167
2018/1/7 Rust By Example
fn function() {
println!("called `function()`");
}
mod cool {
pub fn function() {
println!("called `cool::function()`");
}
}
mod my {
fn function() {
println!("called `my::function()`");
}
mod cool {
pub fn function() {
println!("called `my::cool::function()`");
}
}
pub fn indirect_call() {
// Let's access all the functions named `function` from this scope!
print!("called `my::indirect_call()`, that\n> ");
// The `self` keyword refers to the current module scope - in this case `my`.
// Calling `self::function()` and calling `function()` directly both give
// the same result, because they refer to the same function.
self::function();
function();
// The `super` keyword refers to the parent scope (outside the `my` module).
super::function();
fn main() {
my::indirect_call();
}
File hierarchy
Modules can be mapped to a le/directory hierarchy. Let's break down the visibility example
in les:
$ tree .
.
|-- my
| |-- inaccessible.rs
| |-- mod.rs
| `-- nested.rs
`-- split.rs
https://rustbyexample.com/print.html 62/167
2018/1/7 Rust By Example
In split.rs :
// This declaration will look for a file named `my.rs` or `my/mod.rs` and will
// insert its contents inside a module named `my` under this scope
mod my;
fn function() {
println!("called `function()`");
}
fn main() {
my::function();
function();
my::indirect_access();
my::nested::function();
}
In my/mod.rs :
// Similarly `mod inaccessible` and `mod nested` will locate the `nested.rs`
// and `inaccessible.rs` files and insert them here under their respective
// modules
mod inaccessible;
pub mod nested;
pub fn function() {
println!("called `my::function()`");
}
fn private_function() {
println!("called `my::private_function()`");
}
pub fn indirect_access() {
print!("called `my::indirect_access()`, that\n> ");
private_function();
}
In my/nested.rs :
pub fn function() {
println!("called `my::nested::function()`");
}
#[allow(dead_code)]
fn private_function() {
println!("called `my::nested::private_function()`");
}
In my/inaccessible.rs :
https://rustbyexample.com/print.html 63/167
2018/1/7 Rust By Example
#[allow(dead_code)]
pub fn public_function() {
println!("called `my::inaccessible::public_function()`");
}
Crates
A crate is a compilation unit in Rust. Whenever rustc some_file.rs is called,
some_file.rs is treated as the crate le. If some_file.rs has mod declarations in it, then
the contents of the module les will get merged with the crate le before running the
compiler over it. In other words, modules do not get compiled individually, only crates get
compiled.
A crate can be compiled into a binary or into a library. By default, rustc will produce a
binary from a crate. This behavior can be overridden by passing the --crate-type ag to
rustc .
Library
Let's create a library, and then see how to link it to another crate.
pub fn public_function() {
println!("called rary's `public_function()`");
}
fn private_function() {
println!("called rary's `private_function()`");
}
pub fn indirect_access() {
print!("called rary's `indirect_access()`, that\n> ");
private_function();
}
Libraries get pre xed with "lib", and by default they get named after their crate le, but this
default name can be overridden using the crate_name attribute.
https://rustbyexample.com/print.html 64/167
2018/1/7 Rust By Example
extern crate
To link a crate to this new library, the extern crate declaration must be used. This will not
only link the library, but also import all its items under a module named the same as the
library. The visibility rules that apply to modules also apply to libraries.
fn main() {
rary::public_function();
rary::indirect_access();
}
# Where library.rlib is the path to the compiled library, assumed that it's
# in the same directory here:
$ rustc executable.rs --extern rary=library.rlib && ./executable
called rary's `public_function()`
called rary's `indirect_access()`, that
> called rary's `private_function()`
Attributes
An attribute is metadata applied to some module, crate or item. This metadata can be used
to/for:
When attributes apply to a whole crate, their syntax is #![crate_attribute] , and when
they apply to a module or item, the syntax is #[item_attribute] (notice the missing bang
! ).
#[attribute = "value"]
#[attribute(key = "value")]
#[attribute(value)]
https://rustbyexample.com/print.html 65/167
2018/1/7 Rust By Example
dead_code
The compiler provides a dead_code lint that will warn about unused functions. An attribute
can be used to disable the lint.
fn used_function() {}
// `#[allow(dead_code)]` is an attribute that disables the `dead_code` lint
#[allow(dead_code)]
fn unused_function() {}
fn noisy_unused_function() {}
// FIXME ^ Add an attribute to suppress the warning
fn main() {
used_function();
}
Note that in real programs, you should eliminate dead code. In these examples we'll allow
dead code in some places because of the interactive nature of the examples.
Crates
The crate_type attribute can be used to tell the compiler whether a crate is a binary or a
library (and even which type of library), and the crate_name attribute can be used to set the
name of the crate.
However, it is important to note that both the crate_type and crate_name attributes have
no e ect whatsoever when using Cargo, the Rust package manager. Since Cargo is used for
the majority of Rust projects, this means real-world uses of crate_type and crate_name
are relatively limited.
pub fn public_function() {
println!("called rary's `public_function()`");
}
fn private_function() {
println!("called rary's `private_function()`");
}
pub fn indirect_access() {
print!("called rary's `indirect_access()`, that\n> ");
private_function();
}
When the crate_type attribute is used, we no longer need to pass the --crate-type ag
to rustc .
https://rustbyexample.com/print.html 66/167
2018/1/7 Rust By Example
$ rustc lib.rs
$ ls lib*
library.rlib
cfg
Conditional compilation is possible through two di erent operators:
// And this function only gets compiled if the target OS is *not* linux
#[cfg(not(target_os = "linux"))]
fn are_you_on_linux() {
println!("You are *not* running linux!");
}
fn main() {
are_you_on_linux();
See also:
Custom
Some conditionals like target_os are implicitly provided by rustc , but custom
conditionals must be passed to rustc using the --cfg ag.
#[cfg(some_condition)]
fn conditional_function() {
println!("condition met!");
}
fn main() {
conditional_function();
}
https://rustbyexample.com/print.html 67/167
2018/1/7 Rust By Example
Try to run this to see what happens without the custom cfg ag.
Generics
Generics is the topic of generalizing types and functionalities to broader cases. This is
extremely useful for reducing code duplication in many ways, but can call for rather
involving syntax. Namely, being generic requires taking great care to specify over which
types a generic type is actually considered valid. The simplest and most common use of
generics is for type parameters.
A type parameter is speci ed as generic by the use of angle brackets and upper camel case:
<Aaa, Bbb, ...> . "Generic type parameters" are typically represented as <T> . In Rust,
"generic" also describes anything that accepts one or more generic type parameters <T> .
Any type speci ed as a generic type parameter is generic, and everything else is concrete
(non-generic).
For example, de ning a generic function named foo that takes an argument T of any type:
fn foo<T>(arg: T) { ... }
Because T has been speci ed as a generic type parameter using <T> , it is considered
generic when used here as (arg: T) . This is the case even if T has previously been
de ned as a struct .
https://rustbyexample.com/print.html 68/167
2018/1/7 Rust By Example
// A concrete type `A`.
struct A;
// In defining the type `Single`, the first use of `A` is not preceded by `<A>`.
// Therefore, `Single` is a concrete type, and `A` is defined as above.
struct Single(A);
// ^ Here is `Single`s first use of the type `A`.
// Here, `<T>` precedes the first use of `T`, so `SingleGen` is a generic type.
// Because the type parameter `T` is generic, it could be anything, including
// the concrete type `A` defined at the top.
struct SingleGen<T>(T);
fn main() {
// `Single` is concrete and explicitly takes `A`.
let _s = Single(A);
See also:
struct s
Functions
The same set of rules can be applied to functions: a type T becomes generic when
preceded by <T> .
Using generic functions sometimes requires explicitly specifying type parameters. This may
be the case if the function is called where the return type is generic, or if the compiler
doesn't have enough information to infer the necessary type parameters.
A function call with explicitly speci ed type parameters looks like: fun::<A, B, ...>() .
https://rustbyexample.com/print.html 69/167
2018/1/7 Rust By Example
struct A; // Concrete type `A`.
struct S(A); // Concrete type `S`.
struct SGen<T>(T); // Generic type `SGen`.
// The following functions all take ownership of the variable passed into
// them and immediately go out of scope, freeing the variable.
fn main() {
// Using the non-generic functions
reg_fn(S(A)); // Concrete type.
gen_spec_t(SGen(A)); // Implicitly specified type parameter `A`.
gen_spec_i32(SGen(6)); // Implicitly specified type parameter `i32`.
See also:
Implementation
Similar to functions, implementations require care to remain generic.
struct S; // Concrete type `S`
struct GenericVal<T>(T,); // Generic type `GenericVal`
https://rustbyexample.com/print.html 70/167
2018/1/7 Rust By Example
struct Val {
val: f64
}
struct GenVal<T>{
gen_val: T
}
// impl of Val
impl Val {
fn value(&self) -> &f64 { &self.val }
}
fn main() {
let x = Val { val: 3.0 };
let y = GenVal { gen_val: 3i32 };
See also:
Traits
Of course trait s can also be generic. Here we de ne one which reimplements the Drop
trait as a generic method to drop itself and an input.
https://rustbyexample.com/print.html 71/167
2018/1/7 Rust By Example
// Non-copyable types.
struct Empty;
struct Null;
fn main() {
let empty = Empty;
let null = Null;
//empty;
//null;
// ^ TODO: Try uncommenting these lines.
}
See also:
Bounds
When working with generics, the type parameters often must use traits as bounds to
stipulate what functionality a type implements. For example, the following example uses the
trait Display to print and so it requires T to be bound by Display ; that is, T must
implement Display .
Bounding restricts the generic to types that conform to the bounds. That is:
https://rustbyexample.com/print.html 72/167
2018/1/7 Rust By Example
Another e ect of bounding is that generic instances are allowed to access the methods of
traits speci ed in the bounds. For example:
trait HasArea {
fn area(&self) -> f64;
}
#[derive(Debug)]
struct Rectangle { length: f64, height: f64 }
#[allow(dead_code)]
struct Triangle { length: f64, height: f64 }
fn main() {
let rectangle = Rectangle { length: 3.0, height: 4.0 };
let _triangle = Triangle { length: 3.0, height: 4.0 };
print_debug(&rectangle);
println!("Area: {}", area(&rectangle));
//print_debug(&_triangle);
//println!("Area: {}", area(&_triangle));
// ^ TODO: Try uncommenting these.
// | Error: Does not implement either `Debug` or `HasArea`.
}
As an additional note, where clauses can also be used to apply bounds in some cases to be
more expressive.
See also:
https://rustbyexample.com/print.html 73/167
2018/1/7 Rust By Example
struct Cardinal;
struct BlueJay;
struct Turkey;
trait Red {}
trait Blue {}
// These functions are only valid for types which implement these
// traits. The fact that the traits are empty is irrelevant.
fn red<T: Red>(_: &T) -> &'static str { "red" }
fn blue<T: Blue>(_: &T) -> &'static str { "blue" }
fn main() {
let cardinal = Cardinal;
let blue_jay = BlueJay;
let _turkey = Turkey;
See also:
Multiple bounds
Multiple bounds can be applied with a + . Like normal, di erent types are separated with , .
fn main() {
let string = "words";
let array = [1, 2, 3];
let vec = vec![1, 2, 3];
compare_prints(&string);
//compare_prints(&array);
// TODO ^ Try uncommenting this.
compare_types(&array, &vec);
}
https://rustbyexample.com/print.html 74/167
2018/1/7 Rust By Example
See also:
Where clauses
A bound can also be expressed using a where clause immediately before the opening { ,
rather than at the type's rst mention. Additionally, where clauses can apply bounds to
arbitrary types, rather than just to type parameters.
impl <A: TraitB + TraitC, D: TraitE + TraitF> MyTrait<A, D> for YourType {}
When using a where clause is more expressive than using normal syntax. The impl in
this example cannot be directly expressed without a where clause:
use std::fmt::Debug;
trait PrintInOption {
fn print_in_option(self);
}
fn main() {
let vec = vec![1, 2, 3];
vec.print_in_option();
}
See also:
The newtype idiom gives compile time guarantees that the right type of value is supplied to
a program.
For example, an age veri cation function that checks age in years, must be given a value of
type Years .
struct Years(i64);
struct Days(i64);
impl Years {
pub fn to_days(&self) -> Days {
Days(self.0 * 365)
}
}
impl Days {
/// truncates partial years
pub fn to_years(&self) -> Years {
Years(self.0 / 365)
}
}
fn main() {
let age = Years(5);
let age_days = age.to_days();
println!("Old enough {}", old_enough(&age));
println!("Old enough {}", old_enough(&age_days.to_years()));
// println!("Old enough {}", old_enough(&age_days));
}
Uncomment the last print statement to observe that the type supplied must be Years .
See also:
structs
Associated items
"Associated Items" refers to a set of rules pertaining to item s of various types. It is an
extension to trait generics, and allows trait s to internally de ne new items.
One such item is called an associated type, providing simpler usage patterns when the
trait is generic over its container type.
See also:
https://rustbyexample.com/print.html 76/167
2018/1/7 Rust By Example
RFC
The Problem
A trait that is generic over its container type has type speci cation requirements - users
of the trait must specify all of its generic types.
In the example below, the Contains trait allows the use of the generic types A and B .
The trait is then implemented for the Container type, specifying i32 for A and B so that
it can be used with fn difference() .
Because Contains is generic, we are forced to explicitly state all of the generic types for
fn difference() . In practice, we want a way to express that A and B are determined by
the input C . As you will see in the next section, associated types provide exactly that
capability.
// `C` contains `A` and `B`. In light of that, having to express `A` and
// `B` again is a nuisance.
fn difference<A, B, C>(container: &C) -> i32 where
C: Contains<A, B> {
container.last() - container.first()
}
fn main() {
let number_1 = 3;
let number_2 = 10;
https://rustbyexample.com/print.html 77/167
2018/1/7 Rust By Example
See also:
Associated types
The use of "Associated types" improves the overall readability of code by moving inner types
locally into a trait as output types. Syntax for the trait de nition is as follows:
// `A` and `B` are defined in the trait via the `type` keyword.
// (Note: `type` in this context is different from `type` when used for
// aliases).
trait Contains {
type A;
type B;
Note that functions that use the trait Contains are no longer required to express A or
B at all:
Let's rewrite the example from the previous section using associated types:
https://rustbyexample.com/print.html 78/167
2018/1/7 Rust By Example
struct Container(i32, i32);
// A trait which checks if 2 items are stored inside of container.
// Also retrieves first or last value.
trait Contains {
// Define generic types here which methods will be able to utilize.
type A;
type B;
fn main() {
let number_1 = 3;
let number_2 = 10;
Data types can use extra generic type parameters to act as markers or to perform type
checking at compile time. These extra parameters hold no storage values, and have no
runtime behavior.
https://rustbyexample.com/print.html 79/167
2018/1/7 Rust By Example
use std::marker::PhantomData;
// A phantom tuple struct which is generic over `A` with hidden parameter `B`.
#[derive(PartialEq)] // Allow equality test for this type.
struct PhantomTuple<A, B>(A,PhantomData<B>);
// A phantom type struct which is generic over `A` with hidden parameter `B`.
#[derive(PartialEq)] // Allow equality test for this type.
struct PhantomStruct<A, B> { first: A, phantom: PhantomData<B> }
// Note: Storage is allocated for generic type `A`, but not for `B`.
// Therefore, `B` cannot be used in computations.
fn main() {
// Here, `f32` and `f64` are the hidden parameters.
// PhantomTuple type specified as `<char, f32>`.
let _tuple1: PhantomTuple<char, f32> = PhantomTuple('Q', PhantomData);
// PhantomTuple type specified as `<char, f64>`.
let _tuple2: PhantomTuple<char, f64> = PhantomTuple('Q', PhantomData);
See also:
https://rustbyexample.com/print.html 80/167
2018/1/7 Rust By Example
use std::ops::Add;
use std::marker::PhantomData;
/// The `Add` trait defines the behavior of the `+` operator.
impl<Unit> Add for Length<Unit> {
type Output = Length<Unit>;
fn main() {
// Specifies `one_foot` to have phantom type parameter `Inch`.
let one_foot: Length<Inch> = Length(12.0, PhantomData);
// `one_meter` has phantom type parameter `Mm`.
let one_meter: Length<Mm> = Length(1000.0, PhantomData);
// Addition works.
println!("one foot + one_foot = {:?} in", two_feet.0);
println!("one meter + one_meter = {:?} mm", two_meters.0);
https://rustbyexample.com/print.html 81/167
2018/1/7 Rust By Example
See also:
Borrowing ( & ), Bounds ( X: Y ), enum, impl & self, Overloading, ref, Traits ( X for Y ), and
TupleStructs.
Scoping rules
Scopes play an important part in ownership, borrowing, and lifetimes. That is, they indicate
to the compiler when borrows are valid, when resources can be freed, and when variables
are created or destroyed.
RAII
Variables in Rust do more than just hold data in the stack: they also own resources, e.g.
Box<T> owns memory in the heap. Rust enforces RAII (Resource Acquisition Is Initialization),
so whenever an object goes out of scope, its destructor is called and its owned resources
are freed.
This behavior shields against resource leak bugs, so you'll never have to manually free
memory or worry about memory leaks again! Here's a quick showcase:
// raii.rs
fn create_box() {
// Allocate an integer on the heap
let _box1 = Box::new(3i32);
fn main() {
// Allocate an integer on the heap
let _box2 = Box::new(5i32);
// A nested scope:
{
// Allocate an integer on the heap
let _box3 = Box::new(4i32);
https://rustbyexample.com/print.html 82/167
2018/1/7 Rust By Example
No leaks here!
Destructor
The notion of a destructor in Rust is provided through the Drop trait. The destructor is
called when the resource goes out of scope. This trait is not required to be implemented for
every type, only implement it for your type if you require its own destructor logic.
Run the below example to see how the Drop trait works. When the variable in the main
function goes out of scope the custom destructor wil be invoked.
struct ToDrop;
impl Drop for ToDrop {
fn drop(&mut self) {
println!("ToDrop is being dropped");
}
}
fn main() {
let x = ToDrop;
println!("Made a ToDrop!");
}
See also:
Box
After moving resources, the previous owner can no longer be used. This avoids creating
dangling pointers.
fn main() {
// _Stack_ allocated integer
let x = 5u32;
// Error! `a` can no longer access the data, because it no longer owns the
// heap memory
//println!("a contains: {}", a);
// TODO ^ Try uncommenting this line
// This function takes ownership of the heap allocated memory from `b`
destroy_box(b);
// Since the heap memory has been freed at this point, this action would
// result in dereferencing freed memory, but it's forbidden by the compiler
// Error! Same reason as the previous Error
//println!("b contains: {}", b);
// TODO ^ Try uncommenting this line
}
Mutability
Mutability of data can be changed when ownership is transferred.
https://rustbyexample.com/print.html 84/167
2018/1/7 Rust By Example
fn main() {
let immutable_box = Box::new(5u32);
// Mutability error
//*immutable_box = 4;
Borrowing
Most of the time, we'd like to access data without taking ownership over it. To accomplish
this, Rust uses a borrowing mechanism. Instead of passing objects by-value ( T ), objects can
be passed by reference ( &T ).
The compiler statically guarantees (via its borrow checker) that references always point to
valid objects. That is, while references to an object exist, the object cannot be destroyed.
fn main() {
// Create a boxed i32, and a stacked i32
let boxed_i32 = Box::new(5_i32);
let stacked_i32 = 6_i32;
{
// Take a reference to the data contained inside the box
let _ref_to_i32: &i32 = &boxed_i32;
// Error!
// Can't destroy `boxed_i32` while the inner value is borrowed.
eat_box_i32(boxed_i32);
// FIXME ^ Comment out this line
https://rustbyexample.com/print.html 85/167
2018/1/7 Rust By Example
Mutability
Mutable data can be mutably borrowed using &mut T . This is called a mutable reference and
gives read/write access to the borrower. In contrast, &T borrows the data via an immutable
reference, and the borrower can read the data but not modify it:
#[allow(dead_code)]
#[derive(Clone, Copy)]
struct Book {
// `&'static str` is a reference to a string allocated in read only memory
author: &'static str,
title: &'static str,
year: u32,
}
// This function takes a reference to a mutable book and changes `year` to 2014
fn new_edition(book: &mut Book) {
book.year = 2014;
println!("I mutably borrowed {} - {} edition", book.title, book.year);
}
fn main() {
// Create an immutable Book named `immutabook`
let immutabook = Book {
// string literals have type `&'static str`
author: "Douglas Hofstadter",
title: "Gödel, Escher, Bach",
year: 1979,
};
See also:
static
Freezing
https://rustbyexample.com/print.html 86/167
2018/1/7 Rust By Example
When data is immutably borrowed, it also freezes. Frozen data can't be modi ed via the
original object until all references to it go out of scope:
fn main() {
let mut _mutable_integer = 7i32;
{
// Borrow `_mutable_integer`
let _large_integer = &_mutable_integer;
Aliasing
Data can be immutably borrowed any number of times, but while immutably borrowed, the
original data can't be mutably borrowed. On the other hand, only one mutable borrow is
allowed at a time. The original data can be borrowed again only after the mutable reference
goes out of scope.
https://rustbyexample.com/print.html 87/167
2018/1/7 Rust By Example
struct Point { x: i32, y: i32, z: i32 }
fn main() {
let mut point = Point { x: 0, y: 0, z: 0 };
{
let borrowed_point = &point;
let another_borrow = &point;
// Data can be accessed via the references and the original owner
println!("Point has coordinates: ({}, {}, {})",
borrowed_point.x, another_borrow.y, point.z);
{
let mutable_borrow = &mut point;
https://rustbyexample.com/print.html 88/167
2018/1/7 Rust By Example
#[derive(Clone, Copy)]
struct Point { x: i32, y: i32 }
fn main() {
let c = 'Q';
{
// `ref` can be paired with `mut` to take mutable references.
let Point { x: _, y: ref mut mut_ref_to_y } = mutable_point;
{
// Destructure `mutable_tuple` to change the value of `last`.
let (_, ref mut last) = mutable_tuple;
*last = 2u32;
}
Lifetimes
A lifetime is a construct the compiler (also called the borrow checker) uses to ensure all
borrows are valid. Speci cally, a variable's lifetime begins when it is created and ends when
it is destroyed. While lifetimes and scopes are often referred to together, they are not the
same.
Take, for example, the case where we borrow a variable via & . The borrow has a lifetime
that is determined by where it is declared. As a result, the borrow is valid as long as it ends
before the lender is destroyed. However, the scope of the borrow is determined by where
the reference is used.
https://rustbyexample.com/print.html 89/167
2018/1/7 Rust By Example
In the following example and in the rest of this section, we will see how lifetimes relate to
scopes, as well as how the two di er.
Note that no names or types are assigned to label lifetimes. This restricts how lifetimes will
be able to be used as we will see.
Explicit annotation
The borrow checker uses explicit lifetime annotations to determine how long references
should be valid. In cases where lifetimes are not elided1, Rust requires explicit annotations
to determine what the lifetime of a reference should be. The syntax for explicitly annotating
a lifetime uses an apostrophe character as follows:
foo<'a>
// `foo` has a lifetime parameter `'a`
Similar to closures, using lifetimes requires generics. Additionally, this lifetime syntax
indicates that the lifetime of foo may not exceed that of 'a . Explicit annotation of a type
has the form &'a T where 'a has already been introduced.
foo<'a, 'b>
// `foo` has lifetime parameters `'a` and `'b`
In this case, the lifetime of foo cannot exceed that of either 'a or 'b .
https://rustbyexample.com/print.html 90/167
2018/1/7 Rust By Example
// `print_refs` takes two references to `i32` which have different
// lifetimes `'a` and `'b`. These two lifetimes must both be at
// least as long as the function `print_refs`.
fn print_refs<'a, 'b>(x: &'a i32, y: &'b i32) {
println!("x is {} and y is {}", x, y);
}
fn main() {
// Create variables to be borrowed below.
let (four, nine) = (4, 9);
failed_borrow();
// `failed_borrow` contains no references to force `'a` to be
// longer than the lifetime of the function, but `'a` is longer.
// Because the lifetime is never constrained, it defaults to `'static`.
}
See also:
Functions
Ignoring elision, function signatures with lifetimes have a few constraints:
Additionally, note that returning references without input is banned if it would result in
returning references to invalid data. The following example shows o some valid forms of
functions with lifetimes:
https://rustbyexample.com/print.html 91/167
2018/1/7 Rust By Example
// One input reference with lifetime `'a` which must live
// at least as long as the function.
fn print_one<'a>(x: &'a i32) {
println!("`print_one`: x is {}", x);
}
fn main() {
let x = 7;
let y = 9;
print_one(&x);
print_multi(&x, &y);
let mut t = 3;
add_one(&mut t);
print_one(&t);
}
See also:
functions
Methods
Methods are annotated similarly to functions:
https://rustbyexample.com/print.html 92/167
2018/1/7 Rust By Example
struct Owner(i32);
impl Owner {
// Annotate lifetimes as in a standalone function.
fn add_one<'a>(&'a mut self) { self.0 += 1; }
fn print<'a>(&'a self) {
println!("`print`: {}", self.0);
}
}
fn main() {
let mut owner = Owner(18);
owner.add_one();
owner.print();
}
See also:
methods
Structs
Annotation of lifetimes in structures are also similar to functions:
fn main() {
let x = 18;
let y = 15;
https://rustbyexample.com/print.html 93/167
2018/1/7 Rust By Example
See also:
structs
Bounds
Just like generic types can be bounded, lifetimes (themselves generic) use bounds as well.
The : character has a slightly di erent meaning here, but + is the same. Note how the
following read:
fn main() {
let x = 7;
let ref_x = Ref(&x);
print_ref(&ref_x);
print(ref_x);
}
See also:
Coercion
https://rustbyexample.com/print.html 94/167
2018/1/7 Rust By Example
A longer lifetime can be coerced into a shorter one so that it works inside a scope it
normally wouldn't work in. This comes in the form of inferred coercion by the Rust compiler,
and also in the form of declaring a lifetime di erence:
fn main() {
let first = 2; // Longer lifetime
{
let second = 3; // Shorter lifetime
static
A 'static lifetime is the longest possible lifetime, and lasts for the lifetime of the running
program. A 'static lifetime may also be coerced to a shorter lifetime. There are two ways
to make a variable with 'static lifetime, and both are stored in the read-only memory of
the binary:
https://rustbyexample.com/print.html 95/167
2018/1/7 Rust By Example
// Make a constant with `'static` lifetime.
static NUM: i32 = 18;
fn main() {
{
// Make a `string` literal and print it:
let static_string = "I'm in read-only memory";
println!("static_string: {}", static_string);
{
// Make an integer to use for `coerce_static`:
let lifetime_num = 9;
See also:
'static constants
elision
Some lifetime patterns are overwelmingly common and so the borrow checker will implicitly
add them to save typing and to improve readability. This process of implicit addition is
called elision. Elision exists in Rust solely because these patterns are common.
The following code shows a few examples of elision. For a more comprehensive description
of elision, see lifetime elision in the book.
https://rustbyexample.com/print.html 96/167
2018/1/7 Rust By Example
// `elided_input` and `annotated_input` essentially have identical signatures
// because the lifetime of `elided_input` is elided by the compiler:
fn elided_input(x: &i32) {
println!("`elided_input`: {}", x);
}
fn main() {
let x = 3;
elided_input(&x);
annotated_input(&x);
See also:
elision
Traits
A trait is a collection of methods de ned for an unknown type: Self . They can access
other methods declared in the same trait.
Traits can be implemented for any data type. In the example below, we de ne Animal , a
group of methods. The Animal trait is then implemented for the Sheep data type,
allowing the use of methods from Animal with a Sheep .
https://rustbyexample.com/print.html 97/167
2018/1/7 Rust By Example
struct Sheep { naked: bool, name: &'static str }
trait Animal {
// Static method signature; `Self` refers to the implementor type.
fn new(name: &'static str) -> Self;
impl Sheep {
fn is_naked(&self) -> bool {
self.naked
}
fn shear(&mut self) {
if self.is_naked() {
// Implementor methods can use the implementor's trait methods.
println!("{} is already naked...", self.name());
} else {
println!("{} gets a haircut!", self.name);
self.naked = true;
}
}
}
fn main() {
// Type annotation is necessary in this case.
let mut dolly: Sheep = Animal::new("Dolly");
// TODO ^ Try removing the type annotations.
dolly.talk();
dolly.shear();
dolly.talk();
}
https://rustbyexample.com/print.html 98/167
2018/1/7 Rust By Example
Derive
The compiler is capable of providing basic implementations for some traits via the
#[derive] attribute. These traits can still be manually implemented if a more complex
behavior is required.
https://rustbyexample.com/print.html 99/167
2018/1/7 Rust By Example
impl Inches {
fn to_centimeters(&self) -> Centimeters {
let &Inches(inches) = self;
fn main() {
let _one_second = Seconds(1);
let cmp =
if foot.to_centimeters() < meter {
"smaller"
} else {
"bigger"
};
See also:
derive
Operator Overloading
https://rustbyexample.com/print.html 100/167
2018/1/7 Rust By Example
In Rust, many of the operators can be overloaded via traits. That is, some operators can be
used to accomplish di erent tasks based on their input arguments. This is possible because
operators are syntactic sugar for method calls. For example, the + operator in a + b calls
the add method (as in a.add(b) ). This add method is part of the Add trait. Hence, the +
operator can be used by any implementor of the Add trait.
A list of the traits, such as Add , that overload operators is available here.
use std::ops;
struct Foo;
struct Bar;
#[derive(Debug)]
struct FooBar;
#[derive(Debug)]
struct BarFoo;
FooBar
}
}
BarFoo
}
}
fn main() {
println!("Foo + Bar = {:?}", Foo + Bar);
println!("Bar + Foo = {:?}", Bar + Foo);
}
See Also
Drop
The Drop trait only has one method: drop , which is called automatically when an object
goes out of scope. The main use of the Drop trait is to free the resources that the
https://rustbyexample.com/print.html 101/167
2018/1/7 Rust By Example
Box , Vec , String , File , and Process are some examples of types that implement the
Drop trait to free resources. The Drop trait can also be manually implemented for any
custom data type.
The following example adds a print to console to the drop function to announce when it is
called.
struct Droppable {
name: &'static str,
}
fn main() {
let _a = Droppable { name: "a" };
// block A
{
let _b = Droppable { name: "b" };
// block B
{
let _c = Droppable { name: "c" };
let _d = Droppable { name: "d" };
Iterators
The Iterator trait is used to implement iterators over collections such as arrays.
The trait requires only a method to be de ned for the next element, which may be
manually de ned in an impl block or automatically de ned (as in arrays and ranges).
As a point of convenience for common situations, the for construct turns some collections
into iterators using the .into_iterator() method.
https://rustbyexample.com/print.html 102/167
2018/1/7 Rust By Example
Methods that can be accessed using the Iterator trait in addition to those shown in the
example below can be found here.
https://rustbyexample.com/print.html 103/167
2018/1/7 Rust By Example
struct Fibonacci {
curr: u32,
next: u32,
}
self.curr = self.next;
self.next = new_next;
fn main() {
// `0..3` is an `Iterator` that generates: 0, 1, and 2.
let mut sequence = 0..3;
// The `skip(n)` method shortens an `Iterator` by dropping its first `n` terms.
println!("The next four terms of the Fibonacci sequence are: ");
for i in fibonacci().skip(4).take(4) {
println!("> {}", i);
}
https://rustbyexample.com/print.html 104/167
2018/1/7 Rust By Example
Clone
When dealing with resources, the default behavior is to transfer them during assignments
or function calls. However, sometimes we need to make a copy of the resource as well.
The Clone trait helps us do exactly this. Most commonly, we can use the .clone() method
de ned by the Clone trait.
fn main() {
// Instantiate `Nil`
let nil = Nil;
// Copy `Nil`, there are no resources to move
let copied_nil = nil;
// Instantiate `Pair`
let pair = Pair(Box::new(1), Box::new(2));
println!("original: {:?}", pair);
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 are expanded into source code that gets
compiled with the rest of the program. However, unlike macros in C and other languages,
Rust macros are expanded into abstract syntax trees, rather than string preprocessing, so
you don't get unexpected precedence bugs.
https://rustbyexample.com/print.html 105/167
2018/1/7 Rust By Example
fn main() {
// This call will expand into `println!("Hello");`
say_hello!()
}
1. Don't repeat yourself. There are many cases where you may need similar functionality
in multiple places but with di erent types. Often, writing a macro is a useful way to
avoid repeating code. (More on this later)
Syntax
In following subsections, we will show how to de ne macros in Rust. There are three basic
ideas:
Designators
The arguments of a macro are pre xed by a dollar sign $ and type annotated with a
designator:
https://rustbyexample.com/print.html 106/167
2018/1/7 Rust By Example
macro_rules! create_function {
// This macro takes an argument of designator `ident` and
// creates a function named `$func_name`.
// The `ident` designator is used for variable/function names.
($func_name:ident) => (
fn $func_name() {
// The `stringify!` macro converts an `ident` into a string.
println!("You called {:?}()",
stringify!($func_name));
}
)
}
// Create functions named `foo` and `bar` with the above macro.
create_function!(foo);
create_function!(bar);
macro_rules! print_result {
// This macro takes an expression of type `expr` and prints
// it as a string along with its result.
// The `expr` designator is used for expressions.
($expression:expr) => (
// `stringify!` will convert the expression *as it is* into a string.
println!("{:?} = {:?}",
stringify!($expression),
$expression);
)
}
fn main() {
foo();
bar();
print_result!(1u32 + 1);
x * x + 2 * x - 1
});
}
block
expr is used for expressions
ident is used for variable/function names
item
pat (pattern)
path
stmt (statement)
tt (token tree)
ty (type)
Overload
Macros can be overloaded to accept di erent combinations of arguments. In that regard,
macro_rules! can work similarly to a match block:
https://rustbyexample.com/print.html 107/167
2018/1/7 Rust By Example
// `test!` will compare `$left` and `$right`
// in different ways depending on how you invoke it:
macro_rules! test {
// Arguments don't need to be separated by a comma.
// Any template can be used!
($left:expr; and $right:expr) => (
println!("{:?} and {:?} is {:?}",
stringify!($left),
stringify!($right),
$left && $right)
);
// ^ each arm must end with a semicolon.
($left:expr; or $right:expr) => (
println!("{:?} or {:?} is {:?}",
stringify!($left),
stringify!($right),
$left || $right)
);
}
fn main() {
test!(1i32 + 1 == 2i32; and 2i32 * 2 == 4i32);
test!(true; or false);
}
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 match one or more
expression, separated by commas. Also note that the semicolon is optional on the last case.
fn main() {
println!("{}", find_min!(1u32));
println!("{}", find_min!(1u32 + 2 , 2u32));
println!("{}", find_min!(5u32, 2u32 * 3, 4u32));
}
https://rustbyexample.com/print.html 108/167
2018/1/7 Rust By Example
use std::ops::{Add, Mul, Sub};
macro_rules! assert_equal_len {
// The `tt` (token tree) designator is used for
// operators and tokens.
($a:ident, $b: ident, $func:ident, $op:tt) => (
assert!($a.len() == $b.len(),
"{:?}: dimension mismatch: {:?} {:?} {:?}",
stringify!($func),
($a.len(),),
stringify!($op),
($b.len(),));
)
}
macro_rules! op {
($func:ident, $bound:ident, $op:tt, $method:ident) => (
fn $func<T: $bound<T, Output=T> + Copy>(xs: &mut Vec<T>, ys: &Vec<T>) {
assert_equal_len!(xs, ys, $func, $op);
mod test {
use std::iter;
macro_rules! test {
($func: ident, $x:expr, $y:expr, $z:expr) => {
#[test]
fn $func() {
for size in 0usize..10 {
let mut x: Vec<_> = iter::repeat($x).take(size).collect();
let y: Vec<_> = iter::repeat($y).take(size).collect();
let z: Vec<_> = iter::repeat($z).take(size).collect();
super::$func(&mut x, &y);
assert_eq!(x, z);
}
}
}
}
https://rustbyexample.com/print.html 109/167
2018/1/7 Rust By Example
Suppose that I want to de ne a little calculator API. I would like to supply an expression an
have the output printed to console.
macro_rules! calculate {
(eval $e:expr) => {{
{
let val: usize = $e; // Force types to be integers
println!("{} = {}", stringify!{$e}, val);
}
}};
}
fn main() {
calculate! {
eval 1 + 2 // hehehe `eval` is _not_ a Rust keyword!
}
calculate! {
eval (1 + 2) * (3 / 4)
}
}
Output:
1 + 2 = 3
(1 + 2) * (3 / 4) = 0
This was a very simple example, but much more complex interfaces have been developed,
such as lazy_static or clap .
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 section to be variadic:
https://rustbyexample.com/print.html 110/167
2018/1/7 Rust By Example
macro_rules! calculate {
// The pattern for a single `eval`
(eval $e:expr) => {{
{
let val: usize = $e; // Force types to be integers
println!("{} = {}", stringify!{$e}, val);
}
}};
fn main() {
calculate! { // Look ma! Variadic `calculate!`!
eval 1 + 2,
eval 3 + 4,
eval (2 * 3) + 1
}
}
Output:
1 + 2 = 3
3 + 4 = 7
(2 * 3) + 1 = 7
Error handling
Error handling is the process of handling the possibility of failure. For example, failing to
read a le and then continuing to use that bad input would clearly be problematic. Noticing
and explicitly managing those errors saves the rest of the program from various pitfalls.
There are various ways to deal with errors in Rust, which are described in the following
subchapters. They all have more or less subtle di erences and di erent use cases. As a rule
of thumb:
An explicit panic is mainly useful for tests and dealing with unrecoverable errors. For
prototyping it can be useful, for example when dealing with functions that haven't been
implemented yet, but in those cases the more descriptive unimplemented is better. In tests
panic is a reasonable way to explicitly fail.
The Option type is for when a value is optional or when the lack of a value is not an error
condition. For example the parent of a directory - / and C: don't have one. When dealing
with Option s, unwrap is ne for prototyping and cases where it's absolutely certain that
there is guaranteed to be a value. However expect is more useful since it lets you specify
an error message in case something goes wrong anyway.
When there is a chance that things do go wrong and the caller has to deal with the problem,
use Result . You can unwrap and expect them as well (please don't do that unless it's a
test or quick prototype).
https://rustbyexample.com/print.html 111/167
2018/1/7 Rust By Example
For a more rigorous discussion of error handling, refer to the error handling section in the
o cial book.
panic
The simplest error handling mechanism we will see is panic . It prints an error message,
starts unwinding the task, and usually exits the program. Here, we explicitly call panic on
our error condition:
fn give_princess(gift: &str) {
// Princesses hate snakes, so we need to stop if she disapproves!
if gift == "snake" { panic!("AAAaaaaa!!!!"); }
fn main() {
give_princess("teddy bear");
give_princess("snake");
}
We could test this against the null string ( "" ) as we do with a snake. Since we're using Rust,
let's instead have the compiler point out cases where there's no gift.
An enum called Option<T> in the std library is used when absence is a possibility. It
manifests itself as one of two "options":
These cases can either be explicitly handled via match or implicitly with unwrap . Implicit
handling will either return the inner element or panic .
Note that it's possible to manually customize panic with expect, but unwrap otherwise
leaves us with a less meaningful output than explicit handling. In the following example,
explicit handling yields a more controlled result while retaining the option to panic if
desired.
https://rustbyexample.com/print.html 112/167
2018/1/7 Rust By Example
// The commoner has seen it all, and can handle any gift well.
// All gifts are handled explicitly using `match`.
fn give_commoner(gift: Option<&str>) {
// Specify a course of action for each case.
match gift {
Some("snake") => println!("Yuck! I'm throwing that snake in a fire."),
Some(inner) => println!("{}? How nice.", inner),
None => println!("No gift? Oh well."),
}
}
fn main() {
let food = Some("cabbage");
let snake = Some("snake");
let void = None;
give_commoner(food);
give_commoner(snake);
give_commoner(void);
give_princess(bird);
give_princess(nothing);
}
Combinators: map
match is a valid method for handling Option s. However, you may eventually nd heavy
usage tedious, especially with operations only valid with an input. In these cases,
combinators can be used to manage control ow in a modular fashion.
Option has a built in method called map() , a combinator for the simple mapping of
Some -> Some and None -> None . Multiple map() calls can be chained together for even
more exibility.
In the following example, process() replaces all functions previous to it while staying
compact.
https://rustbyexample.com/print.html 113/167
2018/1/7 Rust By Example
#![allow(dead_code)]
#[derive(Debug)] enum Food { Apple, Carrot, Potato }
// Cooking food. Here, we showcase `map()` instead of `match` for case handling.
fn cook(chopped: Option<Chopped>) -> Option<Cooked> {
chopped.map(|Chopped(food)| Cooked(food))
}
fn main() {
let apple = Some(Food::Apple);
let carrot = Some(Food::Carrot);
let potato = None;
eat(cooked_apple);
eat(cooked_carrot);
eat(cooked_potato);
}
See also:
https://rustbyexample.com/print.html 114/167
2018/1/7 Rust By Example
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 become confusing. That's where another
combinator called and_then() , known in some languages as atmap, comes in.
and_then() calls its function input with the wrapped value and returns the result. If the
Option is None , then it returns None instead.
https://rustbyexample.com/print.html 115/167
2018/1/7 Rust By Example
#![allow(dead_code)]
#[derive(Debug)] enum Food { CordonBleu, Steak, Sushi }
#[derive(Debug)] enum Day { Monday, Tuesday, Wednesday }
fn main() {
let (cordon_bleu, steak, sushi) = (Food::CordonBleu, Food::Steak, Food::Sushi);
eat(cordon_bleu, Day::Monday);
eat(steak, Day::Tuesday);
eat(sushi, Day::Wednesday);
}
See also:
Result
Result is a richer version of the Option type that describes possible error instead of
possible absence.
https://rustbyexample.com/print.html 116/167
2018/1/7 Rust By Example
Like Option , Result has many methods associated with it. unwrap() , for example, either
yields the element T or panic s. For case handling, there are many combinators between
Result and Option that overlap.
In working with Rust, you will likely encounter methods that return the Result type, such as
the parse() method. It might not always be possible to parse a string into the other type,
so parse() returns a Result indicating possible failure.
Let's see what happens when we successfully and unsuccessfully parse() a string:
fn main() {
let twenty = multiply("10", "2");
println!("double is {}", twenty);
In the unsuccessful case, parse() leaves us with an error for unwrap() to panic on.
Additionally, the panic exits our program and provides an unpleasant error message.
To improve the quality of our error message, we should be more speci c about the return
type and consider explicitly handling the error.
We rst need to know what kind of error type we are dealing with. To determine the Err
type, we look to parse() , which is implemented with the FromStr trait for i32 . As a result,
the Err type is speci ed as ParseIntError .
In the example below, the straightforward match statement leads to code that is overall
more cumbersome.
https://rustbyexample.com/print.html 117/167
2018/1/7 Rust By Example
use std::num::ParseIntError;
// With the return type rewritten, we use pattern matching without `unwrap()`.
fn multiply(first_number_str: &str, second_number_str: &str) -> Result<i32, ParseIntError> {
match first_number_str.parse::<i32>() {
Ok(first_number) => {
match second_number_str.parse::<i32>() {
Ok(second_number) => {
Ok(first_number * second_number)
},
Err(e) => Err(e),
}
},
Err(e) => Err(e),
}
}
fn main() {
// This still presents a reasonable answer.
let twenty = multiply("10", "2");
print(twenty);
Luckily, Option 's map , and_then , and many other combinators are also implemented for
Result . Result contains a complete listing.
use std::num::ParseIntError;
// As with `Option`, we can use combinators such as `map()`.
// This function is otherwise identical to the one above and reads:
// Modify n if the value is valid, otherwise pass on the error.
fn multiply(first_number_str: &str, second_number_str: &str) -> Result<i32, ParseIntError> {
first_number_str.parse::<i32>().and_then(|first_number| {
second_number_str.parse::<i32>().map(|second_number| first_number * second_number)
})
}
fn main() {
// This still presents a reasonable answer.
let twenty = multiply("10", "2");
print(twenty);
https://rustbyexample.com/print.html 118/167
2018/1/7 Rust By Example
At a module level, creating aliases can be particularly helpful. Errors found in a speci c
module often have the same Err type, so a single alias can succinctly de ne all associated
Results . This is so useful that the std library even supplies one: io::Result !
use std::num::ParseIntError;
// Define a generic alias for a `Result` with the error type `ParseIntError`.
type AliasedResult<T> = Result<T, ParseIntError>;
fn main() {
print(multiply("10", "2"));
print(multiply("t", "2"));
}
See also:
io::Result
Early returns
In the previous example, we explicitly handled the errors using combinators. Another way to
deal with this case analysis is to use a combination of match statements and early returns.
That is, we can simply stop executing the function and return the error if one occurs. For
some, this form of code can be easier to both read and write. Consider this version of the
previous example, rewritten using early returns:
https://rustbyexample.com/print.html 119/167
2018/1/7 Rust By Example
use std::num::ParseIntError;
fn multiply(first_number_str: &str, second_number_str: &str) -> Result<i32, ParseIntError> {
let first_number = match first_number_str.parse::<i32>() {
Ok(first_number) => first_number,
Err(e) => return Err(e),
};
Ok(first_number * second_number)
}
fn main() {
print(multiply("10", "2"));
print(multiply("t", "2"));
}
At this point, we've learned to explicitly handle errors using combinators and early returns.
While we generally want to avoid panicking, explicitly handling all of our errors is
cumbersome.
In the next section, we'll introduce ? for the cases where we simply need to unwrap
without possibly inducing panic .
Introducing ?
Sometimes we just want the simplicity of unwrap without the possibility of a panic . Until
now, unwrap has forced us to nest deeper and deeper when what we really wanted was to
get the variable out. This is exactly the purpose of ? .
1
? is almost exactly equivalent to an unwrap which return s instead of panic s on Err s.
Let's see how we can simplify the earlier example that used combinators:
https://rustbyexample.com/print.html 120/167
2018/1/7 Rust By Example
use std::num::ParseIntError;
fn multiply(first_number_str: &str, second_number_str: &str) -> Result<i32, ParseIntError> {
let first_number = first_number_str.parse::<i32>()?;
let second_number = second_number_str.parse::<i32>()?;
Ok(first_number * second_number)
}
fn main() {
print(multiply("10", "2"));
print(multiply("t", "2"));
}
use std::num::ParseIntError;
fn multiply(first_number_str: &str, second_number_str: &str) -> Result<i32, ParseIntError> {
let first_number = try!(first_number_str.parse::<i32>());
let second_number = try!(second_number_str.parse::<i32>());
Ok(first_number * second_number)
}
fn main() {
print(multiply("10", "2"));
print(multiply("t", "2"));
}
https://rustbyexample.com/print.html 121/167
2018/1/7 Rust By Example
In the following code, two instances of unwrap generate di erent error types. Vec::first
returns an Option , while parse::<i32> returns a Result<i32, ParseIntError> :
fn main() {
let numbers = vec!["42", "93", "18"];
let empty = vec![];
let strings = vec!["tofu", "93", "18"];
Over the next sections, we'll see several strategies for handling these kind of problems.
use std::num::ParseIntError;
fn double_first(vec: Vec<&str>) -> Option<Result<i32, ParseIntError>> {
vec.first().map(|first| {
first.parse::<i32>().map(|n| 2 * n)
})
}
fn main() {
let numbers = vec!["42", "93", "18"];
let empty = vec![];
let strings = vec!["tofu", "93", "18"];
There are times when we'll want to stop processing on errors (like with ? ) but keep going
when the Option is None . A couple of combinators come in handy to swap the Result and
Option .
https://rustbyexample.com/print.html 122/167
2018/1/7 Rust By Example
use std::num::ParseIntError;
fn double_first(vec: Vec<&str>) -> Result<Option<i32>, ParseIntError> {
let opt = vec.first().map(|first| {
first.parse::<i32>().map(|n| 2 * n)
});
Ok(opt)
}
fn main() {
let numbers = vec!["42", "93", "18"];
let empty = vec![];
let strings = vec!["tofu", "93", "18"];
Rust allows us to de ne our own error types. In general, a "good" error type:
https://rustbyexample.com/print.html 123/167
2018/1/7 Rust By Example
use std::error;
use std::fmt;
use std::num::ParseIntError;
#[derive(Debug, Clone)]
// Define our error types. These may be customized for our error handling cases.
// Now we will be able to write our own errors, defer to an underlying error
// implementation, or do something in between.
struct DoubleError;
fn print(result: Result<i32>) {
match result {
Ok(n) => println!("The first doubled is {}", n),
Err(e) => println!("Error: {}", e),
}
}
fn main() {
let numbers = vec!["42", "93", "18"];
let empty = vec![];
let strings = vec!["tofu", "93", "18"];
print(double_first(numbers));
print(double_first(empty));
print(double_first(strings));
}
determined.
The stdlib helps in boxing our errors by having Box implement conversion from any type
that implements the Error trait into the trait object Box<Error> , via From .
use std::error;
use std::fmt;
use std::num::ParseIntError;
#[derive(Debug, Clone)]
struct EmptyVec;
fn print(result: Result<i32>) {
match result {
Ok(n) => println!("The first doubled is {}", n),
Err(e) => println!("Error: {}", e),
}
}
fn main() {
let numbers = vec!["42", "93", "18"];
let empty = vec![];
let strings = vec!["tofu", "93", "18"];
print(double_first(numbers));
print(double_first(empty));
print(double_first(strings));
}
See also:
Other uses of ?
https://rustbyexample.com/print.html 125/167
2018/1/7 Rust By Example
Notice in the previous example that our immediate reaction to calling parse is to map the
error from a library error into a boxed error:
.and_then(|s| s.parse::<i32>()
.map_err(|e| e.into())
Since this is a simple and common operation, it would be convenient if it could be elided.
Alas, because and_then is not su ciently exible, it cannot. However, we can instead use ?
.
? was previously explained as either unwrap or return Err(err) . This is only mostly true.
It actually means unwrap or return Err(From::from(err)) . Since From::from is a
conversion utility between di erent types, this means that if you ? where the error is
convertible to the return type, it will convert automatically.
Here, we rewrite the previous example using ? . As a result, the map_err will go away when
From::from is implemented for our error type:
https://rustbyexample.com/print.html 126/167
2018/1/7 Rust By Example
use std::error;
use std::fmt;
use std::num::ParseIntError;
#[derive(Debug)]
struct EmptyVec;
// The same structure as before but rather than chain all `Results`
// and `Options` along, we `?` to get the inner value out immediately.
fn double_first(vec: Vec<&str>) -> Result<i32> {
let first = vec.first().ok_or(EmptyVec)?;
let parsed = first.parse::<i32>()?;
Ok(2 * parsed)
}
fn print(result: Result<i32>) {
match result {
Ok(n) => println!("The first doubled is {}", n),
Err(e) => println!("Error: {}", e),
}
}
fn main() {
let numbers = vec!["42", "93", "18"];
let empty = vec![];
let strings = vec!["tofu", "93", "18"];
print(double_first(numbers));
print(double_first(empty));
print(double_first(strings));
}
This is actually fairly clean now. Compared with the original panic , it is very similar to
replacing the unwrap calls with ? except that the return types are Result . As a result, they
must be destructured at the top level.
See also:
From::from and ?
Wrapping errors
https://rustbyexample.com/print.html 127/167
2018/1/7 Rust By Example
https://rustbyexample.com/print.html 128/167
2018/1/7 Rust By Example
use std::error;
use std::num::ParseIntError;
use std::fmt;
#[derive(Debug)]
enum DoubleError {
EmptyVec,
// We will defer to the parse error implementation for their error.
// Supplying extra info requires adding more data to the type.
Parse(ParseIntError),
}
Ok(2 * parsed)
}
fn print(result: Result<i32>) {
match result {
Ok(n) => println!("The first doubled is {}", n),
Err(e) => println!("Error: {}", e),
}
}
fn main() {
let numbers = vec!["42", "93", "18"];
let empty = vec![];
let strings = vec!["tofu", "93", "18"];
https://rustbyexample.com/print.html 129/167
2018/1/7 Rust By Example
print(double_first(numbers));
print(double_first(empty));
print(double_first(strings));
}
This adds a bit more boilerplate for handling errors and might not be needed in all
applications. There are some libraries that can take care of the boiler plate for you.
See also:
fn main() {
let strings = vec!["tofu", "93", "18"];
let possible_numbers: Vec<_> = strings
.into_iter()
.map(|s| s.parse::<i32>())
.collect();
println!("Results: {:?}", possible_numbers);
}
fn main() {
let strings = vec!["tofu", "93", "18"];
let numbers: Vec<_> = strings
.into_iter()
.map(|s| s.parse::<i32>())
.filter_map(Result::ok)
.collect();
println!("Results: {:?}", numbers);
}
fn main() {
let strings = vec!["tofu", "93", "18"];
let numbers: Result<Vec<_>, _> = strings
.into_iter()
.map(|s| s.parse::<i32>())
.collect();
println!("Results: {:?}", numbers);
}
When you look at the results, you'll note that everything is still wrapped in Result . A little
more boilerplate is needed for this.
fn main() {
let strings = vec!["tofu", "93", "18"];
let (numbers, errors): (Vec<_>, Vec<_>) = strings
.into_iter()
.map(|s| s.parse::<i32>())
.partition(Result::is_ok);
let numbers: Vec<_> = numbers.into_iter().map(Result::unwrap).collect();
let errors: Vec<_> = errors.into_iter().map(Result::unwrap_err).collect();
println!("Numbers: {:?}", numbers);
println!("Errors: {:?}", errors);
}
https://rustbyexample.com/print.html 131/167
2018/1/7 Rust By Example
See also:
Boxed values can be dereferenced using the * operator; this removes one layer of
indirection.
https://rustbyexample.com/print.html 132/167
2018/1/7 Rust By Example
use std::mem;
#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
struct Point {
x: f64,
y: f64,
}
#[allow(dead_code)]
struct Rectangle {
p1: Point,
p2: Point,
}
fn main() {
// (all the type annotations are superfluous)
// Stack allocated variables
let point: Point = origin();
let rectangle: Rectangle = Rectangle {
p1: origin(),
p2: Point { x: 3.0, y: 4.0 }
};
// Double indirection
let box_in_a_box: Box<Box<Point>> = Box::new(boxed_origin());
Vectors
https://rustbyexample.com/print.html 133/167
2018/1/7 Rust By Example
Vectors are re-sizable arrays. Like slices, their size is not known at compile time, but they
can grow or shrink at any time. A vector is represented using 3 words: a pointer to the data,
its length, and its capacity. The capacity indicates how much memory is reserved for the
vector. The vector can grow as long as the length is smaller than the capacity. When this
threshold needs to be surpassed, the vector is reallocated with a larger capacity.
fn main() {
// Iterators can be collected into vectors
let collected_iterator: Vec<i32> = (0..10).collect();
println!("Collected (0..10) into: {:?}", collected_iterator);
// `pop` removes the last element from the vector and returns it
println!("Pop last element: {:?}", xs.pop());
Strings
There are two types of strings in Rust: String and &str .
https://rustbyexample.com/print.html 134/167
2018/1/7 Rust By Example
A String is stored as a vector of bytes ( Vec<u8> ), but guaranteed to always be a valid UTF-
8 sequence. String is heap allocated, growable and not null terminated.
&str is a slice ( &[u8] ) that always points to a valid UTF-8 sequence, and can be used to
view into a String , just like &[T] is a view into Vec<T> .
fn main() {
// (all the type annotations are superfluous)
// A reference to a string allocated in read only memory
let pangram: &'static str = "the quick brown fox jumps over the lazy dog";
println!("Pangram: {}", pangram);
More str / String methods can be found under the std::str and std::string modules
Generally special characters are escaped with a backslash character: \ . This way you can
add any character to your string, even unprintable ones and ones that you don't know how
to type. If you want a literal backslash, escape it with another one: \\
https://rustbyexample.com/print.html 135/167
2018/1/7 Rust By Example
String or character literal delimiters occuring within a literal must be escaped: "\"" , '\'' .
fn main() {
// You can use escapes to write bytes by their hexadecimal values...
let byte_escape = "I'm writing \x52\x75\x73\x74!";
println!("What are you doing\x3F (\\x3F means ?) {}", byte_escape);
Sometimes there are just too many characters that need to be escaped or it's just much
more convenient to write a string out as-is. This is where raw string literals come into play.
fn main() {
let raw_str = r"Escapes don't work here: \x3F \u{211D}";
println!("{}", raw_str);
// If you need "# in your string, just use more #s in the delimiter.
// There is no limit for the number of #s you can use.
let longer_delimiter = r###"A string with "# in it. And even "##!"###;
println!("{}", longer_delimiter);
}
Want a string that's not UTF-8? (Remember, str and String must be valid UTF-8) Or
maybe you want an array of bytes that's mostly text? Byte strings to the rescue!
https://rustbyexample.com/print.html 136/167
2018/1/7 Rust By Example
use std::str;
fn main() {
// Note that this is not actually a &str
let bytestring: &[u8; 20] = b"this is a bytestring";
For conversions between character encodings check out the encoding crate.
A more detailed listing of the ways to write string literals and escape characters is given in
the 'Tokens' chapter of the Rust Reference.
Option
Sometimes it's desirable to catch the failure of some parts of a program instead of calling
panic! ; this can be accomplished using the Option enum.
https://rustbyexample.com/print.html 137/167
2018/1/7 Rust By Example
// An integer division that doesn't `panic!`
fn checked_division(dividend: i32, divisor: i32) -> Option<i32> {
if divisor == 0 {
// Failure is represented as the `None` variant
None
} else {
// Result is wrapped in a `Some` variant
Some(dividend / divisor)
}
}
fn main() {
try_division(4, 2);
try_division(1, 0);
Result
We've seen that the Option enum can be used as a return value from functions that may
fail, where None can be returned to indicate failure. However, sometimes it is important to
express why an operation failed. To do this we have the Result enum.
Ok(value) which indicates that the operation succeeded, and wraps the value
returned by the operation. ( value has type T )
Err(why) , which indicates that the operation failed, and wraps why , which (hopefully)
explains the cause of the failure. ( why has type E )
https://rustbyexample.com/print.html 138/167
2018/1/7 Rust By Example
mod checked {
// Mathematical "errors" we want to catch
#[derive(Debug)]
pub enum MathError {
DivisionByZero,
NonPositiveLogarithm,
NegativeSquareRoot,
}
fn main() {
// Will this fail?
println!("{}", op(1.0, 10.0));
}
?
Chaining results using match can get pretty untidy; luckily, the ? operator can be used to
make things pretty again. ? is used at the end of an expression returning a Result , and is
equivalent to a match expression, where the Err(err) branch expands to an early
return Err(err) , and the Ok(ok) branch expands to an ok expression.
https://rustbyexample.com/print.html 139/167
2018/1/7 Rust By Example
mod checked {
#[derive(Debug)]
enum MathError {
DivisionByZero,
NonPositiveLogarithm,
NegativeSquareRoot,
}
// Intermediate function
fn op_(x: f64, y: f64) -> MathResult {
// if `div` "fails", then `DivisionByZero` will be `return`ed
let ratio = div(x, y)?;
sqrt(ln)
}
fn main() {
checked::op(1.0, 10.0);
}
Be sure to check the documentation, as there are many methods to map/compose Result .
panic!
https://rustbyexample.com/print.html 140/167
2018/1/7 Rust By Example
The panic! macro can be used to generate a panic and start unwinding its stack. While
unwinding, the runtime will take care of freeing all the resources owned by the thread by
calling the destructor of all its objects.
Since we are dealing with programs with only one thread, panic! will cause the program to
report the panic message and exit.
HashMap
Where vectors store values by an integer index, HashMap s store values by key. HashMap
keys can be booleans, integers, strings, or any other type that implements the Eq and Hash
traits. More on this in the next section.
https://rustbyexample.com/print.html 141/167
2018/1/7 Rust By Example
Like vectors, HashMap s are growable, but HashMaps can also shrink themselves when they
have excess space. You can create a HashMap with a certain starting capacity using
HashMap::with_capacity(uint) , or use HashMap::new() to get a HashMap with a default
initial capacity (recommended).
use std::collections::HashMap;
fn call(number: &str) -> &str {
match number {
"798-1364" => "We're sorry, the call cannot be completed as dialed.
Please hang up and try again.",
"645-7689" => "Hello, this is Mr. Awesome's Pizza. My name is Fred.
What can I get for you today?",
_ => "Hi! Who is this again?"
}
}
fn main() {
let mut contacts = HashMap::new();
contacts.insert("Daniel", "798-1364");
contacts.insert("Ashley", "645-7689");
contacts.insert("Katie", "435-8291");
contacts.insert("Robert", "956-1745");
match contacts.get(&"Ashley") {
Some(&number) => println!("Calling Ashley: {}", call(number)),
_ => println!("Don't have Ashley's number."),
}
contacts.remove(&("Ashley"));
For more information on how hashing and hash maps (sometimes called hash tables) work,
have a look at Hash Table Wikipedia
bool (though not very useful since there is only two possible keys)
int , uint , and all variations thereof
String and &str (protip: you can have a HashMap keyed by String and call .get()
with an &str )
https://rustbyexample.com/print.html 142/167
2018/1/7 Rust By Example
Note that f32 and f64 do not implement Hash , likely because oating-point precision
errors would make using them as hashmap keys horribly error-prone.
All collection classes implement Eq and Hash if their contained type also respectively
implements Eq and Hash . For example, Vec<T> will implement Hash if T implements
Hash .
You can easily implement Eq and Hash for a custom type with just one line:
#[derive(PartialEq, Eq, Hash)]
The compiler will do the rest. If you want more control over the details, you can implement
Eq and/or Hash yourself. This guide will not cover the speci cs of implementing Hash .
To play around with using a struct in HashMap , let's try making a very simple user logon
system:
https://rustbyexample.com/print.html 143/167
2018/1/7 Rust By Example
use std::collections::HashMap;
// Eq requires that you derive PartialEq on the type.
#[derive(PartialEq, Eq, Hash)]
struct Account<'a>{
username: &'a str,
password: &'a str,
}
struct AccountInfo<'a>{
name: &'a str,
email: &'a str,
}
fn try_logon<'a>(accounts: &Accounts<'a>,
username: &'a str, password: &'a str){
println!("Username: {}", username);
println!("Password: {}", password);
println!("Attempting logon...");
match accounts.get(&logon) {
Some(account_info) => {
println!("Successful logon!");
println!("Name: {}", account_info.name);
println!("Email: {}", account_info.email);
},
_ => println!("Login failed!"),
}
}
fn main(){
let mut accounts: Accounts = HashMap::new();
accounts.insert(account, account_info);
HashSet
Consider a HashSet as a HashMap where we just care about the keys ( HashSet<T> is, in
actuality, just a wrapper around HashMap<T, ()> ).
"What's the point of that?" you ask. "I could just store the keys in a Vec ."
https://rustbyexample.com/print.html 144/167
2018/1/7 Rust By Example
A HashSet 's unique feature is that it is guaranteed to not have duplicate elements. That's
the contract that any set collection ful lls. HashSet is just one implementation. (see also:
BTreeSet )
If you insert a value that is already present in the HashSet , (i.e. the new value is equal to the
existing and they both have the same hash), then the new value will replace the old.
This is great for when you never want more than one of something, or when you want to
know if you've already got something.
Sets have 4 primary operations (all of the following calls return an iterator):
difference : get all the elements that are in the rst set but not the second.
intersection : get all the elements that are only in both sets.
symmetric_difference : get all the elements that are in one set or the other, but not
both.
use std::collections::HashSet;
fn main() {
let mut a: HashSet<i32> = vec!(1i32, 2, 3).into_iter().collect();
let mut b: HashSet<i32> = vec!(2i32, 3, 4).into_iter().collect();
assert!(a.insert(4));
assert!(a.contains(&4));
b.insert(5);
// Print [1, 5]
println!("Symmetric Difference: {:?}",
a.symmetric_difference(&b).collect::<Vec<&i32>>());
}
https://rustbyexample.com/print.html 145/167
2018/1/7 Rust By Example
Std misc
Many other types are provided by the std library to support things such as:
Threads
Channels
File I/O
See also:
Threads
Rust provides a mechanism for spawning native OS threads via the spawn function, the
argument of this function is a moving closure.
use std::thread;
static NTHREADS: i32 = 10;
for i in 0..NTHREADS {
// Spin up another thread
children.push(thread::spawn(move || {
println!("this is thread number {}", i);
}));
}
Testcase: map-reduce
Rust makes it very easy to parallelise data processing, without many of the headaches
traditionally associated with such an attempt.
https://rustbyexample.com/print.html 146/167
2018/1/7 Rust By Example
The standard library provides great threading primitives out of the box. These, combined
with Rust's concept of Ownership and aliasing rules, automatically prevent data races.
The aliasing rules (one writable reference XOR many readable references) automatically
prevent you from manipulating state that is visible to other threads. (Where synchronisation
is needed, there are synchronisation primitives like Mutex es or Channel s.)
In this example, we will calculate the sum of all digits in a block of numbers. We will do this
by parcelling out chunks of the block into di erent threads. Each thread will sum its tiny
block of digits, and subsequently we will sum the intermediate sums produced by each
thread.
Note that, although we're passing references across thread boundaries, Rust understands
that we're only passing read-only references, and that thus no unsafety or data races can
occur. Because we're move -ing the data segments into the thread, Rust will also ensure the
data is kept alive until the threads exit, so no dangling pointers occur.
https://rustbyexample.com/print.html 147/167
2018/1/7 Rust By Example
use std::thread;
// This is the `main` thread
fn main() {
/*************************************************************************
* "Map" phase
*
* Divide our data into segments, and apply initial processing
************************************************************************/
}));
https://rustbyexample.com/print.html 148/167
2018/1/7 Rust By Example
}
/*************************************************************************
* "Reduce" phase
*
* Collect our intermediate results, and combine them into a final result
************************************************************************/
Assignments
It is not wise to let our number of threads depend on user inputted data. What if the user
decides to insert a lot of spaces? Do we really want to spawn 2,000 threads? Modify the
program so that the data is always chunked into a limited number of chunks, de ned by a
static constant at the beginning of the program.
See also:
Threads
vectors and iterators
closures, move semantics and move closures
destructuring assignments
turbo sh notation to help type inference
unwrap vs. expect
enumerate
Channels
Rust provides asynchronous channels for communication between threads. Channels
allow a unidirectional ow of information between two end-points: the Sender and the
Receiver .
https://rustbyexample.com/print.html 149/167
2018/1/7 Rust By Example
use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc;
use std::thread;
fn main() {
// Channels have two endpoints: the `Sender<T>` and the `Receiver<T>`,
// where `T` is the type of the message to be transferred
// (type annotation is superfluous)
let (tx, rx): (Sender<i32>, Receiver<i32>) = mpsc::channel();
for id in 0..NTHREADS {
// The sender endpoint can be copied
let thread_tx = tx.clone();
Path
The Path struct represents le paths in the underlying lesystem. There are two avors of
Path : posix::Path , for UNIX-like systems, and windows::Path , for Windows. The prelude
exports the appropriate platform-speci c Path variant.
A Path can be created from an OsStr , and provides several methods to get information
from the le/directory the path points to.
Note that a Path is not internally represented as an UTF-8 string, but instead is stored as a
vector of bytes ( Vec<u8> ). Therefore, converting a Path to a &str is not free and may fail
(an Option is returned).
https://rustbyexample.com/print.html 150/167
2018/1/7 Rust By Example
use std::path::Path;
fn main() {
// Create a `Path` from an `&'static str`
let path = Path::new(".");
See also
File I/O
The File struct represents a le that has been opened (it wraps a le descriptor), and gives
read and/or write access to the underlying le.
Since many things can go wrong when doing le I/O, all the File methods return the
io::Result<T> type, which is an alias for Result<T, io::Error> .
This makes the failure of all I/O operations explicit. Thanks to this, the programmer can see
all the failure paths, and is encouraged to handle them in a proactive manner.
open
The open static method can be used to open a le in read-only mode.
A File owns a resource, the le descriptor and takes care of closing the le when it is drop
ed.
https://rustbyexample.com/print.html 151/167
2018/1/7 Rust By Example
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
fn main() {
// Create a path to the desired file
let path = Path::new("hello.txt");
let display = path.display();
// `file` goes out of scope, and the "hello.txt" file gets closed
}
(You are encouraged to test the previous example under di erent failure conditions:
hello.txt doesn't exist, or hello.txt is not readable, etc.)
create
The create static method opens a le in write-only mode. If the le already existed, the old
content is destroyed. Otherwise, a new le is created.
https://rustbyexample.com/print.html 152/167
2018/1/7 Rust By Example
use std::error::Error;
use std::io::prelude::*;
use std::fs::File;
use std::path::Path;
fn main() {
let path = Path::new("out/lorem_ipsum.txt");
let display = path.display();
$ mkdir out
$ rustc create.rs && ./create
successfully wrote to out/lorem_ipsum.txt
$ cat out/lorem_ipsum.txt
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
(As in the previous example, you are encouraged to test this example under failure
conditions.)
There is also a more generic open_mode method that can open les in other modes like:
read+write, append, etc.
https://rustbyexample.com/print.html 153/167
2018/1/7 Rust By Example
Child processes
The process::Output struct represents the output of a nished child process, and the
process::Command struct is a process builder.
use std::process::Command;
fn main() {
let output = Command::new("rustc")
.arg("--version")
.output().unwrap_or_else(|e| {
panic!("failed to execute process: {}", e)
});
if output.status.success() {
let s = String::from_utf8_lossy(&output.stdout);
(You are encouraged to try the previous example with an incorrect ag passed to rustc )
Pipes
The std::Child struct represents a running child process, and exposes the stdin , stdout
and stderr handles for interaction with the underlying process via pipes.
https://rustbyexample.com/print.html 154/167
2018/1/7 Rust By Example
use std::error::Error;
use std::io::prelude::*;
use std::process::{Command, Stdio};
fn main() {
// Spawn the `wc` command
let process = match Command::new("wc")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn() {
Err(why) => panic!("couldn't spawn wc: {}", why.description()),
Ok(process) => process,
};
// Because `stdin` does not live after the above calls, it is `drop`ed,
// and the pipe is closed.
//
// This is very important, otherwise `wc` wouldn't start processing the
// input we just sent.
Wait
If you'd like to wait for a process::Child to nish, you must call Child::wait , which will
return a process::ExitStatus .
https://rustbyexample.com/print.html 155/167
2018/1/7 Rust By Example
use std::process::Command;
fn main() {
let mut child = Command::new("sleep").arg("5").spawn().unwrap();
let _result = child.wait().unwrap();
Filesystem Operations
The std::io::fs module contains several functions that deal with the lesystem.
https://rustbyexample.com/print.html 156/167
2018/1/7 Rust By Example
use std::fs;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::prelude::*;
use std::os::unix;
use std::path::Path;
f.write_all(s.as_bytes())
}
fn main() {
println!("`mkdir a`");
// Create a directory, returns `io::Result<()>`
match fs::create_dir("a") {
Err(why) => println!("! {:?}", why.kind()),
Ok(_) => {},
}
println!("`mkdir -p a/c/d`");
// Recursively create a directory, returns `io::Result<()>`
fs::create_dir_all("a/c/d").unwrap_or_else(|why| {
println!("! {:?}", why.kind());
});
println!("`touch a/c/e.txt`");
touch(&Path::new("a/c/e.txt")).unwrap_or_else(|why| {
println!("! {:?}", why.kind());
});
https://rustbyexample.com/print.html 157/167
2018/1/7 Rust By Example
println!("`cat a/c/b.txt`");
match cat(&Path::new("a/c/b.txt")) {
Err(why) => println!("! {:?}", why.kind()),
Ok(s) => println!("> {}", s),
}
println!("`ls a`");
// Read the contents of a directory, returns `io::Result<Vec<Path>>`
match fs::read_dir("a") {
Err(why) => println!("! {:?}", why.kind()),
Ok(paths) => for path in paths {
println!("> {:?}", path.unwrap().path());
},
}
println!("`rm a/c/e.txt`");
// Remove a file, returns `io::Result<()>`
fs::remove_file("a/c/e.txt").unwrap_or_else(|why| {
println!("! {:?}", why.kind());
});
println!("`rmdir a/c/d`");
// Remove an empty directory, returns `io::Result<()>`
fs::remove_dir("a/c/d").unwrap_or_else(|why| {
println!("! {:?}", why.kind());
});
}
https://rustbyexample.com/print.html 158/167
2018/1/7 Rust By Example
$ tree a
a
|-- b.txt
`-- c
`-- b.txt -> ../b.txt
1 directory, 2 files
See also:
cfg!
Program arguments
Standard Library
The command line arguments can be accessed using std::env::args , which returns an
iterator that yields a String for each argument:
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
// The first argument is the path that was used to call the program.
println!("My path is {}.", args[0]);
// The rest of the arguments are the passed command line parameters.
// Call the program like this:
// $ ./args arg1 arg2
println!("I got {:?} arguments: {:?}.", args.len() - 1, &args[1..]);
}
$ ./args 1 2 3
My path is ./args.
I got 3 arguments: ["1", "2", "3"].
https://rustbyexample.com/print.html 159/167
2018/1/7 Rust By Example
Crates
Alternatively, there are numerous crates that can provide extra functionality when creating
command line applications. The Rust Cookbook exhibits best practices on how to use one of
the more popular command line argument crates, clap .
Argument parsing
Matching can be used to parse simple arguments:
https://rustbyexample.com/print.html 160/167
2018/1/7 Rust By Example
use std::env;
fn increase(number: i32) {
println!("{}", number + 1);
}
fn decrease(number: i32) {
println!("{}", number - 1);
}
fn help() {
println!("usage:
match_args <string>
Check whether given string is the answer.
match_args {{increase|decrease}} <integer>
Increase or decrease given integer by one.");
}
fn main() {
let args: Vec<String> = env::args().collect();
match args.len() {
// no arguments passed
1 => {
println!("My name is 'match_args'. Try passing some arguments!");
},
// one argument passed
2 => {
match args[1].parse() {
Ok(42) => println!("This is the answer!"),
_ => println!("This is not the answer."),
}
},
// one command and one argument passed
3 => {
let cmd = &args[1];
let num = &args[2];
// parse the number
let number: i32 = match num.parse() {
Ok(n) => {
n
},
Err(_) => {
println!("error: second argument not an integer");
help();
return;
},
};
// parse the command
match &cmd[..] {
"increase" => increase(number),
"decrease" => decrease(number),
_ => {
println!("error: invalid command");
help();
},
}
},
// all the other cases
_ => {
// show a help message
help();
}
}
}
https://rustbyexample.com/print.html 161/167
2018/1/7 Rust By Example
$ ./match_args Rust
This is not the answer.
$ ./match_args 42
This is the answer!
$ ./match_args do something
error: second argument not an integer
usage:
match_args <string>
Check whether given string is the answer.
match_args {increase|decrease} <integer>
Increase or decrease given integer by one.
$ ./match_args do 42
error: invalid command
usage:
match_args <string>
Check whether given string is the answer.
match_args {increase|decrease} <integer>
Increase or decrease given integer by one.
$ ./match_args increase 42
43
https://rustbyexample.com/print.html 162/167
2018/1/7 Rust By Example
use std::fmt;
fn main() {
// z = -1 + 0i
let z = Complex { re: -1., im: 0. };
Since calling foreign functions is considered unsafe, it's common to write safe wrappers
around them.
https://rustbyexample.com/print.html 163/167
2018/1/7 Rust By Example
use std::fmt;
#[link(name = "m")]
extern {
fn ccosf(z: Complex) -> Complex;
}
// safe wrapper
fn cos(z: Complex) -> Complex {
unsafe { ccosf(z) }
}
fn main() {
// z = 0 + 1i
let z = Complex { re: 0., im: 1. };
Meta
Some topics aren't exactly relevant to how you program but provide you tooling or
infrastructure support which just makes things better for everyone. These topics include:
Documentation: Generate library documentation for users via the included rustdoc .
Testing: Create testsuites for libraries to give con dence that your library does exactly
what it's supposed to.
Benchmarking: Create benchmarks for functionality to be con dent that they run
quickly.
Documentation
https://rustbyexample.com/print.html 164/167
2018/1/7 Rust By Example
Doc comments are very useful for big projects that require documentation. When running
Rustdoc, these are the comments that get compiled into documentation. They are denoted
by a /// , and support Markdown.
#![crate_name = "doc"]
/// A human being is represented here
pub struct Person {
/// A person must have a name, no matter how much Juliet may hate it
name: String,
}
impl Person {
/// Returns a person with the name given them
///
/// # Arguments
///
/// * `name` - A string slice that holds the name of the person
///
/// # Example
///
/// ```
/// // You can have rust code between fences inside the comments
/// // If you pass --test to Rustdoc, it will even test it for you!
/// use doc::Person;
/// let person = Person::new("name");
/// ```
pub fn new(name: &str) -> Person {
Person {
name: name.to_string(),
}
}
fn main() {
let john = Person::new("John");
john.hello();
}
To run the tests, rst build the code as a library, then tell rustdoc where to nd the library so
it can link it into each doctest program:
(When you run cargo test on a library crate, Cargo will automatically generate and run the
correct rustc and rustdoc commands.)
Testing
Functions can be tested by using these attributes:
https://rustbyexample.com/print.html 165/167
2018/1/7 Rust By Example
#[test] marks a function as a unit test. The function must take zero parameters and
return nothing.
#[should_panic] marks a function as a panicking test.
// Conditionally compile `main` only when the test-suite is *not* being run.
#[cfg(not(test))]
fn main() {
println!("If you see this, the tests were not compiled nor ran!");
}
// Conditionally compile the module `test` only when the test-suite is run.
#[cfg(test)]
mod test {
// A helper function `distance_test` will need.
fn distance(a: (f32, f32), b: (f32, f32)) -> f32 {
(
(b.0 - a.0).powi(2) +
(b.1 - a.1).powi(2)
).sqrt()
}
#[test]
fn distance_test() {
assert!(distance((0f32, 0f32), (1f32, 1f32)) == (2f32).sqrt());
}
#[test]
#[should_panic]
fn failing_test() {
assert!(1i32 == 2i32);
}
}
running 2 tests
test test::distance_test ... ok
test test::failing_test ... ok
$ rustc unit_test.rs
$ ./unit_test
If you see this, the tests were not compiled nor ran!
See also:
Unsafe Operations
https://rustbyexample.com/print.html 166/167
2018/1/7 Rust By Example
As an introduction to this section, to borrow from the o cial docs, "one should try to
minimize the amount of unsafe code in a code base." With that in mind, let's get started!
Unsafe blocks in Rust are used to bypass protections put in place by the compiler;
speci cally, there are four primary things that unsafe blocks are used for:
Raw Pointers
Raw pointers * and references &T function similarly, but references are always safe
because they are guaranteed to point to valid data due to the borrow checker.
Dereferencing a raw pointer can only be done through an unsafe block.
fn main() {
let raw_p: *const u32 = &10;
unsafe {
assert!(*raw_p == 10);
}
}
Transmute
Allows simple conversion from one type to another, however both types must have the
same size and alignment:
fn main() {
let u: &[u8] = &[49, 50, 51];
unsafe {
assert!(u == std::mem::transmute::<&str, &[u8]>("123"));
}
}
https://rustbyexample.com/print.html 167/167