mirror of
https://github.com/ahgamut/rust-ape-example.git
synced 2024-11-24 17:00:58 +00:00
added all examples from rust by example
via panflute and https://github.com/rust-lang/rust-by-example
This commit is contained in:
parent
609e05b0f5
commit
d976dc883b
156 changed files with 7028 additions and 415 deletions
30
src/bin/attribute/cfg.rs
Normal file
30
src/bin/attribute/cfg.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
// ./src/attribute/cfg.md
|
||||
|
||||
|
||||
// This function only gets compiled if the target OS is linux
|
||||
#[cfg(target_os = "linux")]
|
||||
fn are_you_on_linux() {
|
||||
println!("You are running linux!");
|
||||
}
|
||||
|
||||
// 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 part0() {
|
||||
are_you_on_linux();
|
||||
|
||||
println!("Are you sure?");
|
||||
if cfg!(target_os = "linux") {
|
||||
println!("Yes. It's definitely linux!");
|
||||
} else {
|
||||
println!("Yes. It's definitely *not* linux!");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
16
src/bin/attribute/cfg/custom.rs
Normal file
16
src/bin/attribute/cfg/custom.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
// ./src/attribute/cfg/custom.md
|
||||
|
||||
|
||||
#[cfg(some_condition)]
|
||||
fn conditional_function() {
|
||||
println!("condition met!");
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
conditional_function();
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
20
src/bin/attribute/unused.rs
Normal file
20
src/bin/attribute/unused.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
// ./src/attribute/unused.md
|
||||
|
||||
|
||||
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 part0() {
|
||||
used_function();
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
46
src/bin/conversion/from_into.rs
Normal file
46
src/bin/conversion/from_into.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
// ./src/conversion/from_into.md
|
||||
|
||||
|
||||
use std::convert::From;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Number {
|
||||
value: i32,
|
||||
}
|
||||
|
||||
impl From<i32> for Number {
|
||||
fn from(item: i32) -> Self {
|
||||
Number { value: item }
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
let num = Number::from(30);
|
||||
println!("My number is {:?}", num);
|
||||
}
|
||||
|
||||
use std::convert::From;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Number {
|
||||
value: i32,
|
||||
}
|
||||
|
||||
impl From<i32> for Number {
|
||||
fn from(item: i32) -> Self {
|
||||
Number { value: item }
|
||||
}
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
let int = 5;
|
||||
// Try removing the type declaration
|
||||
let num: Number = int.into();
|
||||
println!("My number is {:?}", num);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
part1();
|
||||
}
|
||||
|
33
src/bin/conversion/string.rs
Normal file
33
src/bin/conversion/string.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
// ./src/conversion/string.md
|
||||
|
||||
|
||||
use std::fmt;
|
||||
|
||||
struct Circle {
|
||||
radius: i32
|
||||
}
|
||||
|
||||
impl fmt::Display for Circle {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Circle of radius {}", self.radius)
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
let circle = Circle { radius: 6 };
|
||||
println!("{}", circle.to_string());
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
let parsed: i32 = "5".parse().unwrap();
|
||||
let turbo_parsed = "10".parse::<i32>().unwrap();
|
||||
|
||||
let sum = parsed + turbo_parsed;
|
||||
println!("Sum: {:?}", sum);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
part1();
|
||||
}
|
||||
|
39
src/bin/conversion/try_from_try_into.rs
Normal file
39
src/bin/conversion/try_from_try_into.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
// ./src/conversion/try_from_try_into.md
|
||||
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::convert::TryInto;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct EvenNumber(i32);
|
||||
|
||||
impl TryFrom<i32> for EvenNumber {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
||||
if value % 2 == 0 {
|
||||
Ok(EvenNumber(value))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
// TryFrom
|
||||
|
||||
assert_eq!(EvenNumber::try_from(8), Ok(EvenNumber(8)));
|
||||
assert_eq!(EvenNumber::try_from(5), Err(()));
|
||||
|
||||
// TryInto
|
||||
|
||||
let result: Result<EvenNumber, ()> = 8i32.try_into();
|
||||
assert_eq!(result, Ok(EvenNumber(8)));
|
||||
let result: Result<EvenNumber, ()> = 5i32.try_into();
|
||||
assert_eq!(result, Err(()));
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
29
src/bin/custom_types/constants.rs
Normal file
29
src/bin/custom_types/constants.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
// ./src/custom_types/constants.md
|
||||
|
||||
|
||||
// Globals are declared outside all other scopes.
|
||||
static LANGUAGE: &str = "Rust";
|
||||
const THRESHOLD: i32 = 10;
|
||||
|
||||
fn is_big(n: i32) -> bool {
|
||||
// Access constant in some function
|
||||
n > THRESHOLD
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
let n = 16;
|
||||
|
||||
// Access constant in the main thread
|
||||
println!("This is {}", LANGUAGE);
|
||||
println!("The threshold is {}", THRESHOLD);
|
||||
println!("{} is {}", n, if is_big(n) { "big" } else { "small" });
|
||||
|
||||
// Error! Cannot modify a `const`.
|
||||
// THRESHOLD = 5;
|
||||
// FIXME ^ Comment out this line
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
83
src/bin/custom_types/enum.rs
Normal file
83
src/bin/custom_types/enum.rs
Normal file
|
@ -0,0 +1,83 @@
|
|||
// ./src/custom_types/enum.md
|
||||
|
||||
|
||||
// Create an `enum` to classify a web event. Note how both
|
||||
// names and type information together specify the variant:
|
||||
// `PageLoad != PageUnload` and `KeyPress(char) != Paste(String)`.
|
||||
// Each is different and independent.
|
||||
enum WebEvent {
|
||||
// An `enum` may either be `unit-like`,
|
||||
PageLoad,
|
||||
PageUnload,
|
||||
// like tuple structs,
|
||||
KeyPress(char),
|
||||
Paste(String),
|
||||
// or c-like structures.
|
||||
Click { x: i64, y: i64 },
|
||||
}
|
||||
|
||||
// A function which takes a `WebEvent` enum as an argument and
|
||||
// returns nothing.
|
||||
fn inspect(event: WebEvent) {
|
||||
match event {
|
||||
WebEvent::PageLoad => println!("page loaded"),
|
||||
WebEvent::PageUnload => println!("page unloaded"),
|
||||
// Destructure `c` from inside the `enum`.
|
||||
WebEvent::KeyPress(c) => println!("pressed '{}'.", c),
|
||||
WebEvent::Paste(s) => println!("pasted \"{}\".", s),
|
||||
// Destructure `Click` into `x` and `y`.
|
||||
WebEvent::Click { x, y } => {
|
||||
println!("clicked at x={}, y={}.", x, y);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
enum VeryVerboseEnumOfThingsToDoWithNumbers {
|
||||
Add,
|
||||
Subtract,
|
||||
}
|
||||
|
||||
// Creates a type alias
|
||||
type Operations = VeryVerboseEnumOfThingsToDoWithNumbers;
|
||||
|
||||
fn part1() {
|
||||
// We can refer to each variant via its alias, not its long and inconvenient
|
||||
// name.
|
||||
let x = Operations::Add;
|
||||
}
|
||||
|
||||
enum VeryVerboseEnumOfThingsToDoWithNumbers {
|
||||
Add,
|
||||
Subtract,
|
||||
}
|
||||
|
||||
impl VeryVerboseEnumOfThingsToDoWithNumbers {
|
||||
fn run(&self, x: i32, y: i32) -> i32 {
|
||||
match self {
|
||||
Self::Add => x + y,
|
||||
Self::Subtract => x - y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
part1();
|
||||
}
|
||||
|
33
src/bin/custom_types/enum/c_like.rs
Normal file
33
src/bin/custom_types/enum/c_like.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
// ./src/custom_types/enum/c_like.md
|
||||
|
||||
|
||||
// An attribute to hide warnings for unused code.
|
||||
#![allow(dead_code)]
|
||||
|
||||
// enum with implicit discriminator (starts at 0)
|
||||
enum Number {
|
||||
Zero,
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
|
||||
// enum with explicit discriminator
|
||||
enum Color {
|
||||
Red = 0xff0000,
|
||||
Green = 0x00ff00,
|
||||
Blue = 0x0000ff,
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
// `enums` can be cast as integers.
|
||||
println!("zero is {}", Number::Zero as i32);
|
||||
println!("one is {}", Number::One as i32);
|
||||
|
||||
println!("roses are #{:06x}", Color::Red as i32);
|
||||
println!("violets are #{:06x}", Color::Blue as i32);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
45
src/bin/custom_types/enum/enum_use.rs
Normal file
45
src/bin/custom_types/enum/enum_use.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
// ./src/custom_types/enum/enum_use.md
|
||||
|
||||
|
||||
// An attribute to hide warnings for unused code.
|
||||
#![allow(dead_code)]
|
||||
|
||||
enum Status {
|
||||
Rich,
|
||||
Poor,
|
||||
}
|
||||
|
||||
enum Work {
|
||||
Civilian,
|
||||
Soldier,
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
// Explicitly `use` each name so they are available without
|
||||
// manual scoping.
|
||||
use crate::Status::{Poor, Rich};
|
||||
// Automatically `use` each name inside `Work`.
|
||||
use crate::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!"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
77
src/bin/custom_types/enum/testcase_linked_list.rs
Normal file
77
src/bin/custom_types/enum/testcase_linked_list.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
// ./src/custom_types/enum/testcase_linked_list.md
|
||||
|
||||
|
||||
use crate::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,
|
||||
}
|
||||
|
||||
// Methods can be attached to an enum
|
||||
impl List {
|
||||
// Create an empty list
|
||||
fn new() -> List {
|
||||
// `Nil` has type `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))
|
||||
}
|
||||
|
||||
// Return the length of the list
|
||||
fn len(&self) -> u32 {
|
||||
// `self` has to be matched, because the behavior of this method
|
||||
// depends on the variant of `self`
|
||||
// `self` has type `&List`, and `*self` has type `List`, matching on a
|
||||
// concrete type `T` is preferred over a match on a reference `&T`
|
||||
// after Rust 2018 you can use self here and tail (with no ref) below as well,
|
||||
// rust will infer &s and ref tail.
|
||||
// See https://doc.rust-lang.org/edition-guide/rust-2018/ownership-and-lifetimes/default-match-bindings.html
|
||||
match *self {
|
||||
// Can't take ownership of the tail, because `self` is borrowed;
|
||||
// instead take a reference to the tail
|
||||
Cons(_, ref tail) => 1 + tail.len(),
|
||||
// Base Case: An empty list has zero length
|
||||
Nil => 0
|
||||
}
|
||||
}
|
||||
|
||||
// Return representation of the list as a (heap allocated) string
|
||||
fn stringify(&self) -> String {
|
||||
match *self {
|
||||
Cons(head, ref tail) => {
|
||||
// `format!` is similar to `print!`, but returns a heap
|
||||
// allocated string instead of printing to the console
|
||||
format!("{}, {}", head, tail.stringify())
|
||||
},
|
||||
Nil => {
|
||||
format!("Nil")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
// Create an empty linked list
|
||||
let mut list = List::new();
|
||||
|
||||
// Prepend some elements
|
||||
list = list.prepend(1);
|
||||
list = list.prepend(2);
|
||||
list = list.prepend(3);
|
||||
|
||||
// Show the final state of the list
|
||||
println!("linked list has length: {}", list.len());
|
||||
println!("{}", list.stringify());
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
83
src/bin/custom_types/structs.rs
Normal file
83
src/bin/custom_types/structs.rs
Normal file
|
@ -0,0 +1,83 @@
|
|||
// ./src/custom_types/structs.md
|
||||
|
||||
|
||||
// An attribute to hide warnings for unused code.
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Person {
|
||||
name: String,
|
||||
age: u8,
|
||||
}
|
||||
|
||||
// A unit struct
|
||||
struct Unit;
|
||||
|
||||
// A tuple struct
|
||||
struct Pair(i32, f32);
|
||||
|
||||
// A struct with two fields
|
||||
struct Point {
|
||||
x: f32,
|
||||
y: f32,
|
||||
}
|
||||
|
||||
// Structs can be reused as fields of another struct
|
||||
struct Rectangle {
|
||||
// A rectangle can be specified by where the top left and bottom right
|
||||
// corners are in space.
|
||||
top_left: Point,
|
||||
bottom_right: Point,
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
// Create struct with field init shorthand
|
||||
let name = String::from("Peter");
|
||||
let age = 27;
|
||||
let peter = Person { name, age };
|
||||
|
||||
// Print debug struct
|
||||
println!("{:?}", peter);
|
||||
|
||||
// Instantiate a `Point`
|
||||
let point: Point = Point { x: 10.3, y: 0.4 };
|
||||
|
||||
// Access the fields of the point
|
||||
println!("point coordinates: ({}, {})", point.x, point.y);
|
||||
|
||||
// Make a new point by using struct update syntax to use the fields of our
|
||||
// other one
|
||||
let bottom_right = Point { x: 5.2, ..point };
|
||||
|
||||
// `bottom_right.y` will be the same as `point.y` because we used that field
|
||||
// from `point`
|
||||
println!("second point: ({}, {})", bottom_right.x, bottom_right.y);
|
||||
|
||||
// Destructure the point using a `let` binding
|
||||
let Point { x: left_edge, y: top_edge } = point;
|
||||
|
||||
let _rectangle = Rectangle {
|
||||
// struct instantiation is an expression too
|
||||
top_left: Point { x: left_edge, y: top_edge },
|
||||
bottom_right: bottom_right,
|
||||
};
|
||||
|
||||
// Instantiate a unit struct
|
||||
let _unit = Unit;
|
||||
|
||||
// Instantiate a tuple struct
|
||||
let pair = Pair(1, 0.1);
|
||||
|
||||
// Access the fields of a tuple struct
|
||||
println!("pair contains {:?} and {:?}", pair.0, pair.1);
|
||||
|
||||
// Destructure a tuple struct
|
||||
let Pair(integer, decimal) = pair;
|
||||
|
||||
println!("pair contains {:?} and {:?}", integer, decimal);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
40
src/bin/error/abort_unwind.rs
Normal file
40
src/bin/error/abort_unwind.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
// ./src/error/abort_unwind.md
|
||||
|
||||
|
||||
|
||||
fn drink(beverage: &str) {
|
||||
// You shouldn't drink too much sugary beverages.
|
||||
if beverage == "lemonade" {
|
||||
if cfg!(panic="abort"){ println!("This is not your party. Run!!!!");}
|
||||
else{ println!("Spit it out!!!!");}
|
||||
}
|
||||
else{ println!("Some refreshing {} is all I need.", beverage); }
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
drink("water");
|
||||
drink("lemonade");
|
||||
}
|
||||
|
||||
|
||||
#[cfg(panic = "unwind")]
|
||||
fn ah(){ println!("Spit it out!!!!");}
|
||||
|
||||
#[cfg(not(panic="unwind"))]
|
||||
fn ah(){ println!("This is not your party. Run!!!!");}
|
||||
|
||||
fn drink(beverage: &str){
|
||||
if beverage == "lemonade"{ ah();}
|
||||
else{println!("Some refreshing {} is all I need.", beverage);}
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
drink("water");
|
||||
drink("lemonade");
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
part1();
|
||||
}
|
||||
|
73
src/bin/error/iter_result.rs
Normal file
73
src/bin/error/iter_result.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
// ./src/error/iter_result.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
let strings = vec!["tofu", "93", "18"];
|
||||
let numbers: Vec<_> = strings
|
||||
.into_iter()
|
||||
.map(|s| s.parse::<i32>())
|
||||
.collect();
|
||||
println!("Results: {:?}", numbers);
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
let strings = vec!["tofu", "93", "18"];
|
||||
let numbers: Vec<_> = strings
|
||||
.into_iter()
|
||||
.filter_map(|s| s.parse::<i32>().ok())
|
||||
.collect();
|
||||
println!("Results: {:?}", numbers);
|
||||
}
|
||||
|
||||
fn part2() {
|
||||
let strings = vec!["42", "tofu", "93", "999", "18"];
|
||||
let mut errors = vec![];
|
||||
let numbers: Vec<_> = strings
|
||||
.into_iter()
|
||||
.map(|s| s.parse::<u8>())
|
||||
.filter_map(|r| r.map_err(|e| errors.push(e)).ok())
|
||||
.collect();
|
||||
println!("Numbers: {:?}", numbers);
|
||||
println!("Errors: {:?}", errors);
|
||||
}
|
||||
|
||||
fn part3() {
|
||||
let strings = vec!["tofu", "93", "18"];
|
||||
let numbers: Result<Vec<_>, _> = strings
|
||||
.into_iter()
|
||||
.map(|s| s.parse::<i32>())
|
||||
.collect();
|
||||
println!("Results: {:?}", numbers);
|
||||
}
|
||||
|
||||
fn part4() {
|
||||
let strings = vec!["tofu", "93", "18"];
|
||||
let (numbers, errors): (Vec<_>, Vec<_>) = strings
|
||||
.into_iter()
|
||||
.map(|s| s.parse::<i32>())
|
||||
.partition(Result::is_ok);
|
||||
println!("Numbers: {:?}", numbers);
|
||||
println!("Errors: {:?}", errors);
|
||||
}
|
||||
|
||||
fn part5() {
|
||||
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);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
part1();
|
||||
part2();
|
||||
part3();
|
||||
part4();
|
||||
part5();
|
||||
}
|
||||
|
26
src/bin/error/multiple_error_types.rs
Normal file
26
src/bin/error/multiple_error_types.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
// ./src/error/multiple_error_types.md
|
||||
|
||||
|
||||
fn double_first(vec: Vec<&str>) -> i32 {
|
||||
let first = vec.first().unwrap(); // Generate error 1
|
||||
2 * first.parse::<i32>().unwrap() // Generate error 2
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
let numbers = vec!["42", "93", "18"];
|
||||
let empty = vec![];
|
||||
let strings = vec!["tofu", "93", "18"];
|
||||
|
||||
println!("The first doubled is {}", double_first(numbers));
|
||||
|
||||
println!("The first doubled is {}", double_first(empty));
|
||||
// Error 1: the input vector is empty
|
||||
|
||||
println!("The first doubled is {}", double_first(strings));
|
||||
// Error 2: the element doesn't parse to a number
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
51
src/bin/error/multiple_error_types/boxing_errors.rs
Normal file
51
src/bin/error/multiple_error_types/boxing_errors.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
// ./src/error/multiple_error_types/boxing_errors.md
|
||||
|
||||
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
|
||||
// Change the alias to `Box<error::Error>`.
|
||||
type Result<T> = std::result::Result<T, Box<dyn error::Error>>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct EmptyVec;
|
||||
|
||||
impl fmt::Display for EmptyVec {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "invalid first item to double")
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for EmptyVec {}
|
||||
|
||||
fn double_first(vec: Vec<&str>) -> Result<i32> {
|
||||
vec.first()
|
||||
.ok_or_else(|| EmptyVec.into()) // Converts to Box
|
||||
.and_then(|s| {
|
||||
s.parse::<i32>()
|
||||
.map_err(|e| e.into()) // Converts to Box
|
||||
.map(|i| 2 * i)
|
||||
})
|
||||
}
|
||||
|
||||
fn print(result: Result<i32>) {
|
||||
match result {
|
||||
Ok(n) => println!("The first doubled is {}", n),
|
||||
Err(e) => println!("Error: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
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));
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
57
src/bin/error/multiple_error_types/define_error_type.rs
Normal file
57
src/bin/error/multiple_error_types/define_error_type.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
// ./src/error/multiple_error_types/define_error_type.md
|
||||
|
||||
|
||||
use std::fmt;
|
||||
|
||||
type Result<T> = std::result::Result<T, DoubleError>;
|
||||
|
||||
// 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.
|
||||
#[derive(Debug, Clone)]
|
||||
struct DoubleError;
|
||||
|
||||
// Generation of an error is completely separate from how it is displayed.
|
||||
// There's no need to be concerned about cluttering complex logic with the display style.
|
||||
//
|
||||
// Note that we don't store any extra info about the errors. This means we can't state
|
||||
// which string failed to parse without modifying our types to carry that information.
|
||||
impl fmt::Display for DoubleError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "invalid first item to double")
|
||||
}
|
||||
}
|
||||
|
||||
fn double_first(vec: Vec<&str>) -> Result<i32> {
|
||||
vec.first()
|
||||
// Change the error to our new type.
|
||||
.ok_or(DoubleError)
|
||||
.and_then(|s| {
|
||||
s.parse::<i32>()
|
||||
// Update to the new error type here also.
|
||||
.map_err(|_| DoubleError)
|
||||
.map(|i| 2 * i)
|
||||
})
|
||||
}
|
||||
|
||||
fn print(result: Result<i32>) {
|
||||
match result {
|
||||
Ok(n) => println!("The first doubled is {}", n),
|
||||
Err(e) => println!("Error: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
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));
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
50
src/bin/error/multiple_error_types/option_result.rs
Normal file
50
src/bin/error/multiple_error_types/option_result.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
// ./src/error/multiple_error_types/option_result.md
|
||||
|
||||
|
||||
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 part0() {
|
||||
let numbers = vec!["42", "93", "18"];
|
||||
let empty = vec![];
|
||||
let strings = vec!["tofu", "93", "18"];
|
||||
|
||||
println!("The first doubled is {:?}", double_first(numbers));
|
||||
|
||||
println!("The first doubled is {:?}", double_first(empty));
|
||||
// Error 1: the input vector is empty
|
||||
|
||||
println!("The first doubled is {:?}", double_first(strings));
|
||||
// Error 2: the element doesn't parse to a number
|
||||
}
|
||||
|
||||
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)
|
||||
});
|
||||
|
||||
opt.map_or(Ok(None), |r| r.map(Some))
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
let numbers = vec!["42", "93", "18"];
|
||||
let empty = vec![];
|
||||
let strings = vec!["tofu", "93", "18"];
|
||||
|
||||
println!("The first doubled is {:?}", double_first(numbers));
|
||||
println!("The first doubled is {:?}", double_first(empty));
|
||||
println!("The first doubled is {:?}", double_first(strings));
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
part1();
|
||||
}
|
||||
|
49
src/bin/error/multiple_error_types/reenter_question_mark.rs
Normal file
49
src/bin/error/multiple_error_types/reenter_question_mark.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
// ./src/error/multiple_error_types/reenter_question_mark.md
|
||||
|
||||
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
|
||||
// Change the alias to `Box<dyn error::Error>`.
|
||||
type Result<T> = std::result::Result<T, Box<dyn error::Error>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct EmptyVec;
|
||||
|
||||
impl fmt::Display for EmptyVec {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "invalid first item to double")
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for 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 part0() {
|
||||
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));
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
87
src/bin/error/multiple_error_types/wrap_error.rs
Normal file
87
src/bin/error/multiple_error_types/wrap_error.rs
Normal file
|
@ -0,0 +1,87 @@
|
|||
// ./src/error/multiple_error_types/wrap_error.md
|
||||
|
||||
|
||||
use std::error;
|
||||
use std::error::Error;
|
||||
use std::num::ParseIntError;
|
||||
use std::fmt;
|
||||
|
||||
type Result<T> = std::result::Result<T, DoubleError>;
|
||||
|
||||
#[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),
|
||||
}
|
||||
|
||||
impl fmt::Display for DoubleError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
DoubleError::EmptyVec =>
|
||||
write!(f, "please use a vector with at least one element"),
|
||||
// The wrapped error contains additional information and is available
|
||||
// via the source() method.
|
||||
DoubleError::Parse(..) =>
|
||||
write!(f, "the provided string could not be parsed as int"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for DoubleError {
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
match *self {
|
||||
DoubleError::EmptyVec => None,
|
||||
// The cause is the underlying implementation error type. Is implicitly
|
||||
// cast to the trait object `&error::Error`. This works because the
|
||||
// underlying type already implements the `Error` trait.
|
||||
DoubleError::Parse(ref e) => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implement the conversion from `ParseIntError` to `DoubleError`.
|
||||
// This will be automatically called by `?` if a `ParseIntError`
|
||||
// needs to be converted into a `DoubleError`.
|
||||
impl From<ParseIntError> for DoubleError {
|
||||
fn from(err: ParseIntError) -> DoubleError {
|
||||
DoubleError::Parse(err)
|
||||
}
|
||||
}
|
||||
|
||||
fn double_first(vec: Vec<&str>) -> Result<i32> {
|
||||
let first = vec.first().ok_or(DoubleError::EmptyVec)?;
|
||||
// Here we implicitly use the `ParseIntError` implementation of `From` (which
|
||||
// we defined above) in order to create a `DoubleError`.
|
||||
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);
|
||||
if let Some(source) = e.source() {
|
||||
println!(" Caused by: {}", source);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
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));
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
44
src/bin/error/option_unwrap.rs
Normal file
44
src/bin/error/option_unwrap.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
// ./src/error/option_unwrap.md
|
||||
|
||||
|
||||
// The adult has seen it all, and can handle any drink well.
|
||||
// All drinks are handled explicitly using `match`.
|
||||
fn give_adult(drink: Option<&str>) {
|
||||
// Specify a course of action for each case.
|
||||
match drink {
|
||||
Some("lemonade") => println!("Yuck! Too sugary."),
|
||||
Some(inner) => println!("{}? How nice.", inner),
|
||||
None => println!("No drink? Oh well."),
|
||||
}
|
||||
}
|
||||
|
||||
// Others will `panic` before drinking sugary drinks.
|
||||
// All drinks are handled implicitly using `unwrap`.
|
||||
fn drink(drink: Option<&str>) {
|
||||
// `unwrap` returns a `panic` when it receives a `None`.
|
||||
let inside = drink.unwrap();
|
||||
if inside == "lemonade" { panic!("AAAaaaaa!!!!"); }
|
||||
|
||||
println!("I love {}s!!!!!", inside);
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
let water = Some("water");
|
||||
let lemonade = Some("lemonade");
|
||||
let void = None;
|
||||
|
||||
give_adult(water);
|
||||
give_adult(lemonade);
|
||||
give_adult(void);
|
||||
|
||||
let coffee = Some("coffee");
|
||||
let nothing = None;
|
||||
|
||||
drink(coffee);
|
||||
drink(nothing);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
60
src/bin/error/option_unwrap/and_then.rs
Normal file
60
src/bin/error/option_unwrap/and_then.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
// ./src/error/option_unwrap/and_then.md
|
||||
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[derive(Debug)] enum Food { CordonBleu, Steak, Sushi }
|
||||
#[derive(Debug)] enum Day { Monday, Tuesday, Wednesday }
|
||||
|
||||
// We don't have the ingredients to make Sushi.
|
||||
fn have_ingredients(food: Food) -> Option<Food> {
|
||||
match food {
|
||||
Food::Sushi => None,
|
||||
_ => Some(food),
|
||||
}
|
||||
}
|
||||
|
||||
// We have the recipe for everything except Cordon Bleu.
|
||||
fn have_recipe(food: Food) -> Option<Food> {
|
||||
match food {
|
||||
Food::CordonBleu => None,
|
||||
_ => Some(food),
|
||||
}
|
||||
}
|
||||
|
||||
// To make a dish, we need both the recipe and the ingredients.
|
||||
// We can represent the logic with a chain of `match`es:
|
||||
fn cookable_v1(food: Food) -> Option<Food> {
|
||||
match have_recipe(food) {
|
||||
None => None,
|
||||
Some(food) => match have_ingredients(food) {
|
||||
None => None,
|
||||
Some(food) => Some(food),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// This can conveniently be rewritten more compactly with `and_then()`:
|
||||
fn cookable_v2(food: Food) -> Option<Food> {
|
||||
have_recipe(food).and_then(have_ingredients)
|
||||
}
|
||||
|
||||
fn eat(food: Food, day: Day) {
|
||||
match cookable_v2(food) {
|
||||
Some(food) => println!("Yay! On {:?} we get to eat {:?}.", day, food),
|
||||
None => println!("Oh no. We don't get to eat on {:?}?", day),
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
let (cordon_bleu, steak, sushi) = (Food::CordonBleu, Food::Steak, Food::Sushi);
|
||||
|
||||
eat(cordon_bleu, Day::Monday);
|
||||
eat(steak, Day::Tuesday);
|
||||
eat(sushi, Day::Wednesday);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
94
src/bin/error/option_unwrap/defaults.rs
Normal file
94
src/bin/error/option_unwrap/defaults.rs
Normal file
|
@ -0,0 +1,94 @@
|
|||
// ./src/error/option_unwrap/defaults.md
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Fruit { Apple, Orange, Banana, Kiwi, Lemon }
|
||||
|
||||
fn part0() {
|
||||
let apple = Some(Fruit::Apple);
|
||||
let orange = Some(Fruit::Orange);
|
||||
let no_fruit: Option<Fruit> = None;
|
||||
|
||||
let first_available_fruit = no_fruit.or(orange).or(apple);
|
||||
println!("first_available_fruit: {:?}", first_available_fruit);
|
||||
// first_available_fruit: Some(Orange)
|
||||
|
||||
// `or` moves its argument.
|
||||
// In the example above, `or(orange)` returned a `Some`, so `or(apple)` was not invoked.
|
||||
// But the variable named `apple` has been moved regardless, and cannot be used anymore.
|
||||
// println!("Variable apple was moved, so this line won't compile: {:?}", apple);
|
||||
// TODO: uncomment the line above to see the compiler error
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Fruit { Apple, Orange, Banana, Kiwi, Lemon }
|
||||
|
||||
fn part1() {
|
||||
let apple = Some(Fruit::Apple);
|
||||
let no_fruit: Option<Fruit> = None;
|
||||
let get_kiwi_as_fallback = || {
|
||||
println!("Providing kiwi as fallback");
|
||||
Some(Fruit::Kiwi)
|
||||
};
|
||||
let get_lemon_as_fallback = || {
|
||||
println!("Providing lemon as fallback");
|
||||
Some(Fruit::Lemon)
|
||||
};
|
||||
|
||||
let first_available_fruit = no_fruit
|
||||
.or_else(get_kiwi_as_fallback)
|
||||
.or_else(get_lemon_as_fallback);
|
||||
println!("first_available_fruit: {:?}", first_available_fruit);
|
||||
// Providing kiwi as fallback
|
||||
// first_available_fruit: Some(Kiwi)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Fruit { Apple, Orange, Banana, Kiwi, Lemon }
|
||||
|
||||
fn part2() {
|
||||
let mut my_fruit: Option<Fruit> = None;
|
||||
let apple = Fruit::Apple;
|
||||
let first_available_fruit = my_fruit.get_or_insert(apple);
|
||||
println!("my_fruit is: {:?}", first_available_fruit);
|
||||
println!("first_available_fruit is: {:?}", first_available_fruit);
|
||||
// my_fruit is: Apple
|
||||
// first_available_fruit is: Apple
|
||||
//println!("Variable named `apple` is moved: {:?}", apple);
|
||||
// TODO: uncomment the line above to see the compiler error
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Fruit { Apple, Orange, Banana, Kiwi, Lemon }
|
||||
|
||||
fn part3() {
|
||||
let mut my_fruit: Option<Fruit> = None;
|
||||
let get_lemon_as_fallback = || {
|
||||
println!("Providing lemon as fallback");
|
||||
Fruit::Lemon
|
||||
};
|
||||
let first_available_fruit = my_fruit
|
||||
.get_or_insert_with(get_lemon_as_fallback);
|
||||
println!("my_fruit is: {:?}", first_available_fruit);
|
||||
println!("first_available_fruit is: {:?}", first_available_fruit);
|
||||
// Providing lemon as fallback
|
||||
// my_fruit is: Lemon
|
||||
// first_available_fruit is: Lemon
|
||||
|
||||
// If the Option has a value, it is left unchanged, and the closure is not invoked
|
||||
let mut my_apple = Some(Fruit::Apple);
|
||||
let should_be_apple = my_apple.get_or_insert_with(get_lemon_as_fallback);
|
||||
println!("should_be_apple is: {:?}", should_be_apple);
|
||||
println!("my_apple is unchanged: {:?}", my_apple);
|
||||
// The output is a follows. Note that the closure `get_lemon_as_fallback` is not invoked
|
||||
// should_be_apple is: Apple
|
||||
// my_apple is unchanged: Some(Apple)
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
part1();
|
||||
part2();
|
||||
part3();
|
||||
}
|
||||
|
69
src/bin/error/option_unwrap/map.rs
Normal file
69
src/bin/error/option_unwrap/map.rs
Normal file
|
@ -0,0 +1,69 @@
|
|||
// ./src/error/option_unwrap/map.md
|
||||
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[derive(Debug)] enum Food { Apple, Carrot, Potato }
|
||||
|
||||
#[derive(Debug)] struct Peeled(Food);
|
||||
#[derive(Debug)] struct Chopped(Food);
|
||||
#[derive(Debug)] struct Cooked(Food);
|
||||
|
||||
// Peeling food. If there isn't any, then return `None`.
|
||||
// Otherwise, return the peeled food.
|
||||
fn peel(food: Option<Food>) -> Option<Peeled> {
|
||||
match food {
|
||||
Some(food) => Some(Peeled(food)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Chopping food. If there isn't any, then return `None`.
|
||||
// Otherwise, return the chopped food.
|
||||
fn chop(peeled: Option<Peeled>) -> Option<Chopped> {
|
||||
match peeled {
|
||||
Some(Peeled(food)) => Some(Chopped(food)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
||||
// A function to peel, chop, and cook food all in sequence.
|
||||
// We chain multiple uses of `map()` to simplify the code.
|
||||
fn process(food: Option<Food>) -> Option<Cooked> {
|
||||
food.map(|f| Peeled(f))
|
||||
.map(|Peeled(f)| Chopped(f))
|
||||
.map(|Chopped(f)| Cooked(f))
|
||||
}
|
||||
|
||||
// Check whether there's food or not before trying to eat it!
|
||||
fn eat(food: Option<Cooked>) {
|
||||
match food {
|
||||
Some(food) => println!("Mmm. I love {:?}", food),
|
||||
None => println!("Oh no! It wasn't edible."),
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
let apple = Some(Food::Apple);
|
||||
let carrot = Some(Food::Carrot);
|
||||
let potato = None;
|
||||
|
||||
let cooked_apple = cook(chop(peel(apple)));
|
||||
let cooked_carrot = cook(chop(peel(carrot)));
|
||||
// Let's try the simpler looking `process()` now.
|
||||
let cooked_potato = process(potato);
|
||||
|
||||
eat(cooked_apple);
|
||||
eat(cooked_carrot);
|
||||
eat(cooked_potato);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
53
src/bin/error/option_unwrap/question_mark.rs
Normal file
53
src/bin/error/option_unwrap/question_mark.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
// ./src/error/option_unwrap/question_mark.md
|
||||
|
||||
|
||||
fn next_birthday(current_age: Option<u8>) -> Option<String> {
|
||||
// If `current_age` is `None`, this returns `None`.
|
||||
// If `current_age` is `Some`, the inner `u8` gets assigned to `next_age`
|
||||
let next_age: u8 = current_age? + 1;
|
||||
Some(format!("Next year I will be {}", next_age))
|
||||
}
|
||||
|
||||
struct Person {
|
||||
job: Option<Job>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Job {
|
||||
phone_number: Option<PhoneNumber>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct PhoneNumber {
|
||||
area_code: Option<u8>,
|
||||
number: u32,
|
||||
}
|
||||
|
||||
impl Person {
|
||||
|
||||
// Gets the area code of the phone number of the person's job, if it exists.
|
||||
fn work_phone_area_code(&self) -> Option<u8> {
|
||||
// This would need many nested `match` statements without the `?` operator.
|
||||
// It would take a lot more code - try writing it yourself and see which
|
||||
// is easier.
|
||||
self.job?.phone_number?.area_code
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
let p = Person {
|
||||
job: Some(Job {
|
||||
phone_number: Some(PhoneNumber {
|
||||
area_code: Some(61),
|
||||
number: 439222222,
|
||||
}),
|
||||
}),
|
||||
};
|
||||
|
||||
assert_eq!(p.work_phone_area_code(), Some(61));
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
19
src/bin/error/panic.rs
Normal file
19
src/bin/error/panic.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
// ./src/error/panic.md
|
||||
|
||||
|
||||
fn drink(beverage: &str) {
|
||||
// You shouldn't drink too much sugary beverages.
|
||||
if beverage == "lemonade" { panic!("AAAaaaaa!!!!"); }
|
||||
|
||||
println!("Some refreshing {} is all I need.", beverage);
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
drink("water");
|
||||
drink("lemonade");
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
35
src/bin/error/result.rs
Normal file
35
src/bin/error/result.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
// ./src/error/result.md
|
||||
|
||||
|
||||
fn multiply(first_number_str: &str, second_number_str: &str) -> i32 {
|
||||
// Let's try using `unwrap()` to get the number out. Will it bite us?
|
||||
let first_number = first_number_str.parse::<i32>().unwrap();
|
||||
let second_number = second_number_str.parse::<i32>().unwrap();
|
||||
first_number * second_number
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
let twenty = multiply("10", "2");
|
||||
println!("double is {}", twenty);
|
||||
|
||||
let tt = multiply("t", "2");
|
||||
println!("double is {}", tt);
|
||||
}
|
||||
|
||||
use std::num::ParseIntError;
|
||||
|
||||
fn part1() -> Result<(), ParseIntError> {
|
||||
let number_str = "10";
|
||||
let number = match number_str.parse::<i32>() {
|
||||
Ok(number) => number,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
println!("{}", number);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
part1();
|
||||
}
|
||||
|
35
src/bin/error/result/early_returns.rs
Normal file
35
src/bin/error/result/early_returns.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
// ./src/error/result/early_returns.md
|
||||
|
||||
|
||||
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),
|
||||
};
|
||||
|
||||
let second_number = match second_number_str.parse::<i32>() {
|
||||
Ok(second_number) => second_number,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
Ok(first_number * second_number)
|
||||
}
|
||||
|
||||
fn print(result: Result<i32, ParseIntError>) {
|
||||
match result {
|
||||
Ok(n) => println!("n is {}", n),
|
||||
Err(e) => println!("Error: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
print(multiply("10", "2"));
|
||||
print(multiply("t", "2"));
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
53
src/bin/error/result/enter_question_mark.rs
Normal file
53
src/bin/error/result/enter_question_mark.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
// ./src/error/result/enter_question_mark.md
|
||||
|
||||
|
||||
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 print(result: Result<i32, ParseIntError>) {
|
||||
match result {
|
||||
Ok(n) => println!("n is {}", n),
|
||||
Err(e) => println!("Error: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
print(multiply("10", "2"));
|
||||
print(multiply("t", "2"));
|
||||
}
|
||||
|
||||
// To compile and run this example without errors, while using Cargo, change the value
|
||||
// of the `edition` field, in the `[package]` section of the `Cargo.toml` file, to "2015".
|
||||
|
||||
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 print(result: Result<i32, ParseIntError>) {
|
||||
match result {
|
||||
Ok(n) => println!("n is {}", n),
|
||||
Err(e) => println!("Error: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
print(multiply("10", "2"));
|
||||
print(multiply("t", "2"));
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
part1();
|
||||
}
|
||||
|
32
src/bin/error/result/result_alias.rs
Normal file
32
src/bin/error/result/result_alias.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
// ./src/error/result/result_alias.md
|
||||
|
||||
|
||||
use std::num::ParseIntError;
|
||||
|
||||
// Define a generic alias for a `Result` with the error type `ParseIntError`.
|
||||
type AliasedResult<T> = Result<T, ParseIntError>;
|
||||
|
||||
// Use the above alias to refer to our specific `Result` type.
|
||||
fn multiply(first_number_str: &str, second_number_str: &str) -> AliasedResult<i32> {
|
||||
first_number_str.parse::<i32>().and_then(|first_number| {
|
||||
second_number_str.parse::<i32>().map(|second_number| first_number * second_number)
|
||||
})
|
||||
}
|
||||
|
||||
// Here, the alias again allows us to save some space.
|
||||
fn print(result: AliasedResult<i32>) {
|
||||
match result {
|
||||
Ok(n) => println!("n is {}", n),
|
||||
Err(e) => println!("Error: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
print(multiply("10", "2"));
|
||||
print(multiply("t", "2"));
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
70
src/bin/error/result/result_map.rs
Normal file
70
src/bin/error/result/result_map.rs
Normal file
|
@ -0,0 +1,70 @@
|
|||
// ./src/error/result/result_map.md
|
||||
|
||||
|
||||
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 print(result: Result<i32, ParseIntError>) {
|
||||
match result {
|
||||
Ok(n) => println!("n is {}", n),
|
||||
Err(e) => println!("Error: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
// This still presents a reasonable answer.
|
||||
let twenty = multiply("10", "2");
|
||||
print(twenty);
|
||||
|
||||
// The following now provides a much more helpful error message.
|
||||
let tt = multiply("t", "2");
|
||||
print(tt);
|
||||
}
|
||||
|
||||
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 print(result: Result<i32, ParseIntError>) {
|
||||
match result {
|
||||
Ok(n) => println!("n is {}", n),
|
||||
Err(e) => println!("Error: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
// This still presents a reasonable answer.
|
||||
let twenty = multiply("10", "2");
|
||||
print(twenty);
|
||||
|
||||
// The following now provides a much more helpful error message.
|
||||
let tt = multiply("t", "2");
|
||||
print(tt);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
part1();
|
||||
}
|
||||
|
|
@ -1,146 +0,0 @@
|
|||
#![allow(dead_code)]
|
||||
use std::mem;
|
||||
// https://doc.rust-lang.org/rust-by-example/primitives.html
|
||||
fn example1 () {
|
||||
// Variables can be type annotated.
|
||||
let logical: bool = true;
|
||||
|
||||
let a_float: f64 = 1.0; // Regular annotation
|
||||
let an_integer = 5i32; // Suffix annotation
|
||||
|
||||
// Or a default will be used.
|
||||
let default_float = 3.0; // `f64`
|
||||
let default_integer = 7; // `i32`
|
||||
|
||||
// A type can also be inferred from context
|
||||
let mut inferred_type = 12; // Type i64 is inferred from another line
|
||||
inferred_type = 4294967296i64;
|
||||
|
||||
// A mutable variable's value can be changed.
|
||||
let mut mutable = 12; // Mutable `i32`
|
||||
mutable = 21;
|
||||
}
|
||||
|
||||
// https://doc.rust-lang.org/rust-by-example/primitives/literals.html
|
||||
fn example1b() {
|
||||
// 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
|
||||
|
||||
// Short-circuiting boolean logic
|
||||
println!("true AND false is {}", true && false);
|
||||
println!("true OR false is {}", true || false);
|
||||
println!("NOT true is {}", !true);
|
||||
|
||||
// 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);
|
||||
|
||||
// Use underscores to improve readability!
|
||||
println!("One million is written as {}", 1_000_000u32);
|
||||
}
|
||||
|
||||
// https://doc.rust-lang.org/rust-by-example/primitives/tuples.html
|
||||
// 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)
|
||||
}
|
||||
|
||||
// The following struct is for the activity.
|
||||
#[derive(Debug)]
|
||||
struct Matrix(f32, f32, f32, f32);
|
||||
|
||||
fn example1c() {
|
||||
// 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);
|
||||
|
||||
// Values can be extracted from the tuple using tuple indexing
|
||||
println!("long tuple first value: {}", long_tuple.0);
|
||||
println!("long tuple second value: {}", long_tuple.1);
|
||||
|
||||
// Tuples can be tuple members
|
||||
let tuple_of_tuples = ((1u8, 2u16, 2u32), (4u64, -1i8), -2i16);
|
||||
|
||||
// Tuples are printable
|
||||
println!("tuple of tuples: {:?}", tuple_of_tuples);
|
||||
|
||||
// But long Tuples cannot be printed
|
||||
// let too_long_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
|
||||
// println!("too long tuple: {:?}", too_long_tuple);
|
||||
// TODO ^ Uncomment the above 2 lines to see the compiler error
|
||||
|
||||
let pair = (1, true);
|
||||
println!("pair is {:?}", pair);
|
||||
|
||||
println!("the reversed pair is {:?}", reverse(pair));
|
||||
|
||||
// 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));
|
||||
|
||||
//tuples can be destructured to create bindings
|
||||
let tuple = (1, "hello", 4.5, true);
|
||||
|
||||
let (a, b, c, d) = tuple;
|
||||
println!("{:?}, {:?}, {:?}, {:?}", a, b, c, d);
|
||||
|
||||
let matrix = Matrix(1.1, 1.2, 2.1, 2.2);
|
||||
println!("{:?}", matrix);
|
||||
}
|
||||
|
||||
|
||||
// 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 example1d() {
|
||||
// Fixed-size array (type signature is superfluous)
|
||||
let xs: [i32; 5] = [1, 2, 3, 4, 5];
|
||||
|
||||
// All elements can be initialized to the same value
|
||||
let ys: [i32; 500] = [0; 500];
|
||||
|
||||
// Indexing starts at 0
|
||||
println!("first element of the array: {}", xs[0]);
|
||||
println!("second element of the array: {}", xs[1]);
|
||||
|
||||
// `len` returns the count of elements in the array
|
||||
println!("number of elements in array: {}", xs.len());
|
||||
|
||||
// Arrays are stack allocated
|
||||
println!("array occupies {} bytes", mem::size_of_val(&xs));
|
||||
|
||||
// Arrays can be automatically borrowed as slices
|
||||
println!("borrow the whole array as a slice");
|
||||
analyze_slice(&xs);
|
||||
|
||||
// Slices can point to a section of an array
|
||||
// They are of the form [starting_index..ending_index]
|
||||
// starting_index is the first position in the slice
|
||||
// ending_index is one more than the last position in the slice
|
||||
println!("borrow a section of the array as a slice");
|
||||
analyze_slice(&ys[1 .. 4]);
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
example1();
|
||||
example1b();
|
||||
example1c();
|
||||
example1d();
|
||||
}
|
|
@ -1,160 +0,0 @@
|
|||
#![allow(dead_code)]
|
||||
use std::mem;
|
||||
|
||||
// https://doc.rust-lang.org/rust-by-example/custom_types/structs.html
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Person {
|
||||
name: String,
|
||||
age: u8,
|
||||
}
|
||||
|
||||
// A unit struct
|
||||
struct Unit;
|
||||
|
||||
// A tuple struct
|
||||
struct Pair(i32, f32);
|
||||
|
||||
// A struct with two fields
|
||||
struct Point {
|
||||
x: f32,
|
||||
y: f32,
|
||||
}
|
||||
|
||||
// Structs can be reused as fields of another struct
|
||||
struct Rectangle {
|
||||
// A rectangle can be specified by where the top left and bottom right
|
||||
// corners are in space.
|
||||
top_left: Point,
|
||||
bottom_right: Point,
|
||||
}
|
||||
|
||||
fn example2() {
|
||||
// Create struct with field init shorthand
|
||||
let name = String::from("Peter");
|
||||
let age = 27;
|
||||
let peter = Person { name, age };
|
||||
|
||||
// Print debug struct
|
||||
println!("{:?}", peter);
|
||||
|
||||
// Instantiate a `Point`
|
||||
let point: Point = Point { x: 10.3, y: 0.4 };
|
||||
|
||||
// Access the fields of the point
|
||||
println!("point coordinates: ({}, {})", point.x, point.y);
|
||||
|
||||
// Make a new point by using struct update syntax to use the fields of our
|
||||
// other one
|
||||
let bottom_right = Point { x: 5.2, ..point };
|
||||
|
||||
// `bottom_right.y` will be the same as `point.y` because we used that field
|
||||
// from `point`
|
||||
println!("second point: ({}, {})", bottom_right.x, bottom_right.y);
|
||||
|
||||
// Destructure the point using a `let` binding
|
||||
let Point { x: left_edge, y: top_edge } = point;
|
||||
|
||||
let _rectangle = Rectangle {
|
||||
top_left: Point { x: left_edge, y: top_edge },
|
||||
bottom_right: bottom_right,
|
||||
// struct instantiation is an expression too
|
||||
};
|
||||
|
||||
// Instantiate a unit struct
|
||||
let _unit = Unit;
|
||||
|
||||
// Instantiate a tuple struct
|
||||
let pair = Pair(1, 0.1);
|
||||
|
||||
// Access the fields of a tuple struct
|
||||
println!("pair contains {:?} and {:?}", pair.0, pair.1);
|
||||
|
||||
// Destructure a tuple struct
|
||||
let Pair(integer, decimal) = pair;
|
||||
|
||||
println!("pair contains {:?} and {:?}", integer, decimal);
|
||||
}
|
||||
|
||||
// https://doc.rust-lang.org/rust-by-example/custom_types/enum.html
|
||||
|
||||
// Create an `enum` to classify a web event. Note how both
|
||||
// names and type information together specify the variant:
|
||||
// `PageLoad != PageUnload` and `KeyPress(char) != Paste(String)`.
|
||||
// Each is different and independent.
|
||||
enum WebEvent {
|
||||
// An `enum` may either be `unit-like`,
|
||||
PageLoad,
|
||||
PageUnload,
|
||||
// like tuple structs,
|
||||
KeyPress(char),
|
||||
Paste(String),
|
||||
// or c-like structures.
|
||||
Click { x: i64, y: i64 },
|
||||
}
|
||||
|
||||
// A function which takes a `WebEvent` enum as an argument and
|
||||
// returns nothing.
|
||||
fn inspect(event: WebEvent) {
|
||||
match event {
|
||||
WebEvent::PageLoad => println!("page loaded"),
|
||||
WebEvent::PageUnload => println!("page unloaded"),
|
||||
// Destructure `c` from inside the `enum`.
|
||||
WebEvent::KeyPress(c) => println!("pressed '{}'.", c),
|
||||
WebEvent::Paste(s) => println!("pasted \"{}\".", s),
|
||||
// Destructure `Click` into `x` and `y`.
|
||||
WebEvent::Click { x, y } => {
|
||||
println!("clicked at x={}, y={}.", x, y);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn example2b() {
|
||||
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);
|
||||
}
|
||||
|
||||
// https://doc.rust-lang.org/rust-by-example/custom_types/enum/c_like.html
|
||||
// enum with implicit discriminator (starts at 0)
|
||||
enum Number {
|
||||
Zero,
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
|
||||
|
||||
// enum with explicit discriminator
|
||||
enum Color {
|
||||
Red = 0xff0000,
|
||||
Green = 0x00ff00,
|
||||
Blue = 0x0000ff,
|
||||
}
|
||||
|
||||
|
||||
fn example2c() {
|
||||
// `enums` can be cast as integers.
|
||||
println!("zero is {}", Number::Zero as i32);
|
||||
println!("one is {}", Number::One as i32);
|
||||
|
||||
println!("roses are #{:06x}", Color::Red as i32);
|
||||
println!("violets are #{:06x}", Color::Blue as i32);
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
println!("Hello World! This is an APE built with Rust.");
|
||||
example2();
|
||||
example2b();
|
||||
example2c();
|
||||
0;
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
// https://doc.rust-lang.org/rust-by-example/variable_bindings.html
|
||||
fn example3a() {
|
||||
let an_integer = 1u32;
|
||||
let a_boolean = true;
|
||||
let unit = ();
|
||||
|
||||
// copy `an_integer` into `copied_integer`
|
||||
let copied_integer = an_integer;
|
||||
|
||||
println!("An integer: {:?}", copied_integer);
|
||||
println!("A boolean: {:?}", a_boolean);
|
||||
println!("Meet the unit value: {:?}", 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;
|
||||
|
||||
let noisy_unused_variable = 2u32;
|
||||
// FIXME ^ Prefix with an underscore to suppress the warning
|
||||
// Please note that warnings may not be shown in a browser
|
||||
}
|
||||
|
||||
// https://doc.rust-lang.org/rust-by-example/variable_bindings/mut.html
|
||||
fn example3b() {
|
||||
let _immutable_binding = 1;
|
||||
let mut mutable_binding = 1;
|
||||
|
||||
println!("Before mutation: {}", mutable_binding);
|
||||
|
||||
// Ok
|
||||
mutable_binding += 1;
|
||||
|
||||
println!("After mutation: {}", mutable_binding);
|
||||
|
||||
// Error!
|
||||
// _immutable_binding += 1;
|
||||
// FIXME ^ Comment out this line
|
||||
}
|
||||
|
||||
// https://doc.rust-lang.org/rust-by-example/variable_bindings/scope.html
|
||||
fn example3c() {
|
||||
// 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;
|
||||
|
||||
println!("inner short: {}", short_lived_binding);
|
||||
}
|
||||
// End of the block
|
||||
|
||||
// Error! `short_lived_binding` doesn't exist in this scope
|
||||
// println!("outer short: {}", short_lived_binding);
|
||||
// FIXME ^ Comment out this line
|
||||
|
||||
println!("outer long: {}", long_lived_binding);
|
||||
}
|
||||
|
||||
// https://doc.rust-lang.org/rust-by-example/variable_bindings/freeze.html
|
||||
fn example3d() {
|
||||
let mut _mutable_integer = 7i32;
|
||||
|
||||
{
|
||||
// Shadowing by immutable `_mutable_integer`
|
||||
let _mutable_integer = _mutable_integer;
|
||||
|
||||
// Error! `_mutable_integer` is frozen in this scope
|
||||
// _mutable_integer = 50;
|
||||
// FIXME ^ Comment out this line
|
||||
|
||||
// `_mutable_integer` goes out of scope
|
||||
}
|
||||
|
||||
// Ok! `_mutable_integer` is not frozen in this scope
|
||||
_mutable_integer = 3;
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
example3a();
|
||||
example3b();
|
||||
example3c();
|
||||
example3d();
|
||||
}
|
46
src/bin/expression.rs
Normal file
46
src/bin/expression.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
// ./src/expression.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
// statement
|
||||
// statement
|
||||
// statement
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
// variable binding
|
||||
let x = 5;
|
||||
|
||||
// expression;
|
||||
x;
|
||||
x + 1;
|
||||
15;
|
||||
}
|
||||
|
||||
fn part2() {
|
||||
let x = 5u32;
|
||||
|
||||
let y = {
|
||||
let x_squared = x * x;
|
||||
let x_cube = x_squared * x;
|
||||
|
||||
// This expression will be assigned to `y`
|
||||
x_cube + x_squared + x
|
||||
};
|
||||
|
||||
let z = {
|
||||
// The semicolon suppresses this expression and `()` is assigned to `z`
|
||||
2 * x;
|
||||
};
|
||||
|
||||
println!("x is {:?}", x);
|
||||
println!("y is {:?}", y);
|
||||
println!("z is {:?}", z);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
part1();
|
||||
part2();
|
||||
}
|
||||
|
82
src/bin/flow_control/for.rs
Normal file
82
src/bin/flow_control/for.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
// ./src/flow_control/for.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
// `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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
// `n` will take the values: 1, 2, ..., 100 in each iteration
|
||||
for n in 1..=100 {
|
||||
if n % 15 == 0 {
|
||||
println!("fizzbuzz");
|
||||
} else if n % 3 == 0 {
|
||||
println!("fizz");
|
||||
} else if n % 5 == 0 {
|
||||
println!("buzz");
|
||||
} else {
|
||||
println!("{}", n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn part2() {
|
||||
let names = vec!["Bob", "Frank", "Ferris"];
|
||||
|
||||
for name in names.iter() {
|
||||
match name {
|
||||
&"Ferris" => println!("There is a rustacean among us!"),
|
||||
// TODO ^ Try deleting the & and matching just "Ferris"
|
||||
_ => println!("Hello {}", name),
|
||||
}
|
||||
}
|
||||
|
||||
println!("names: {:?}", names);
|
||||
}
|
||||
|
||||
fn part3() {
|
||||
let names = vec!["Bob", "Frank", "Ferris"];
|
||||
|
||||
for name in names.into_iter() {
|
||||
match name {
|
||||
"Ferris" => println!("There is a rustacean among us!"),
|
||||
_ => println!("Hello {}", name),
|
||||
}
|
||||
}
|
||||
|
||||
// println!("names: {:?}", names);
|
||||
// FIXME ^ Comment out this line
|
||||
}
|
||||
|
||||
fn part4() {
|
||||
let mut names = vec!["Bob", "Frank", "Ferris"];
|
||||
|
||||
for name in names.iter_mut() {
|
||||
*name = match name {
|
||||
&mut "Ferris" => "There is a rustacean among us!",
|
||||
_ => "Hello",
|
||||
}
|
||||
}
|
||||
|
||||
println!("names: {:?}", names);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
part1();
|
||||
part2();
|
||||
part3();
|
||||
part4();
|
||||
}
|
||||
|
36
src/bin/flow_control/if_else.rs
Normal file
36
src/bin/flow_control/if_else.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
// ./src/flow_control/if_else.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
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");
|
||||
|
||||
// This expression returns an `i32`.
|
||||
10 * n
|
||||
} else {
|
||||
println!(", and is a big number, halve the number");
|
||||
|
||||
// This expression must return an `i32` as well.
|
||||
n / 2
|
||||
// TODO ^ Try suppressing this expression with a semicolon.
|
||||
};
|
||||
// ^ Don't forget to put a semicolon here! All `let` bindings need it.
|
||||
|
||||
println!("{} -> {}", n, big_n);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
94
src/bin/flow_control/if_let.rs
Normal file
94
src/bin/flow_control/if_let.rs
Normal file
|
@ -0,0 +1,94 @@
|
|||
// ./src/flow_control/if_let.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
// 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);
|
||||
}
|
||||
|
||||
// If you need to specify a failure, use an else:
|
||||
if let Some(i) = letter {
|
||||
println!("Matched {:?}!", i);
|
||||
} else {
|
||||
// Destructure failed. Change to the failure case.
|
||||
println!("Didn't match a number. Let's go with a letter!");
|
||||
}
|
||||
|
||||
// Provide an altered failing condition.
|
||||
let i_like_letters = false;
|
||||
|
||||
if let Some(i) = emoticon {
|
||||
println!("Matched {:?}!", i);
|
||||
// Destructure failed. Evaluate an `else if` condition to see if the
|
||||
// alternate failure branch should be taken:
|
||||
} else if i_like_letters {
|
||||
println!("Didn't match a number. Let's go with a letter!");
|
||||
} else {
|
||||
// The condition evaluated false. This branch is the default:
|
||||
println!("I don't like letters. Let's go with an emoticon :)!");
|
||||
}
|
||||
}
|
||||
|
||||
// Our example enum
|
||||
enum Foo {
|
||||
Bar,
|
||||
Baz,
|
||||
Qux(u32)
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
// Create example variables
|
||||
let a = Foo::Bar;
|
||||
let b = Foo::Baz;
|
||||
let c = Foo::Qux(100);
|
||||
|
||||
// Variable a matches Foo::Bar
|
||||
if let Foo::Bar = a {
|
||||
println!("a is foobar");
|
||||
}
|
||||
|
||||
// Variable b does not match Foo::Bar
|
||||
// So this will print nothing
|
||||
if let Foo::Bar = b {
|
||||
println!("b is foobar");
|
||||
}
|
||||
|
||||
// Variable c matches Foo::Qux which has a value
|
||||
// Similar to Some() in the previous example
|
||||
if let Foo::Qux(value) = c {
|
||||
println!("c is {}", value);
|
||||
}
|
||||
|
||||
// Binding also works with `if let`
|
||||
if let Foo::Qux(value @ 100) = c {
|
||||
println!("c is one hundred");
|
||||
}
|
||||
}
|
||||
|
||||
// This enum purposely neither implements nor derives PartialEq.
|
||||
// That is why comparing Foo::Bar == a fails below.
|
||||
enum Foo {Bar}
|
||||
|
||||
fn part2() {
|
||||
let a = Foo::Bar;
|
||||
|
||||
// Variable a matches Foo::Bar
|
||||
if Foo::Bar == a {
|
||||
// ^-- this causes a compile-time error. Use `if let` instead.
|
||||
println!("a is foobar");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
part1();
|
||||
part2();
|
||||
}
|
||||
|
34
src/bin/flow_control/loop.rs
Normal file
34
src/bin/flow_control/loop.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
// ./src/flow_control/loop.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
let mut count = 0u32;
|
||||
|
||||
println!("Let's count until infinity!");
|
||||
|
||||
// Infinite loop
|
||||
loop {
|
||||
count += 1;
|
||||
|
||||
if count == 3 {
|
||||
println!("three");
|
||||
|
||||
// Skip the rest of this iteration
|
||||
continue;
|
||||
}
|
||||
|
||||
println!("{}", count);
|
||||
|
||||
if count == 5 {
|
||||
println!("OK, that's enough");
|
||||
|
||||
// Exit this loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
29
src/bin/flow_control/loop/nested.rs
Normal file
29
src/bin/flow_control/loop/nested.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
// ./src/flow_control/loop/nested.md
|
||||
|
||||
|
||||
#![allow(unreachable_code)]
|
||||
|
||||
fn part0() {
|
||||
'outer: loop {
|
||||
println!("Entered the outer loop");
|
||||
|
||||
'inner: loop {
|
||||
println!("Entered the inner loop");
|
||||
|
||||
// This would break only the inner loop
|
||||
//break;
|
||||
|
||||
// This breaks the outer loop
|
||||
break 'outer;
|
||||
}
|
||||
|
||||
println!("This point will never be reached");
|
||||
}
|
||||
|
||||
println!("Exited the outer loop");
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
21
src/bin/flow_control/loop/return.rs
Normal file
21
src/bin/flow_control/loop/return.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
// ./src/flow_control/loop/return.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
let mut counter = 0;
|
||||
|
||||
let result = loop {
|
||||
counter += 1;
|
||||
|
||||
if counter == 10 {
|
||||
break counter * 2;
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(result, 20);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
37
src/bin/flow_control/match.rs
Normal file
37
src/bin/flow_control/match.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
// ./src/flow_control/match.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
let number = 13;
|
||||
// TODO ^ Try different values for `number`
|
||||
|
||||
println!("Tell me about {}", number);
|
||||
match number {
|
||||
// Match a single value
|
||||
1 => println!("One!"),
|
||||
// Match several values
|
||||
2 | 3 | 5 | 7 | 11 => println!("This is a prime"),
|
||||
// TODO ^ Try adding 13 to the list of prime values
|
||||
// Match an inclusive range
|
||||
13..=19 => println!("A teen"),
|
||||
// Handle the rest of cases
|
||||
_ => println!("Ain't special"),
|
||||
// TODO ^ Try commenting out this catch-all arm
|
||||
}
|
||||
|
||||
let boolean = true;
|
||||
// Match is an expression too
|
||||
let binary = match boolean {
|
||||
// The arms of a match must cover all the possible values
|
||||
false => 0,
|
||||
true => 1,
|
||||
// TODO ^ Try commenting out one of these arms
|
||||
};
|
||||
|
||||
println!("{} -> {}", boolean, binary);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
44
src/bin/flow_control/match/binding.rs
Normal file
44
src/bin/flow_control/match/binding.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
// ./src/flow_control/match/binding.md
|
||||
|
||||
|
||||
// A function `age` which returns a `u32`.
|
||||
fn age() -> u32 {
|
||||
15
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
println!("Tell me what type of person you are");
|
||||
|
||||
match age() {
|
||||
0 => println!("I haven't celebrated my first birthday yet"),
|
||||
// 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),
|
||||
}
|
||||
}
|
||||
|
||||
fn some_number() -> Option<u32> {
|
||||
Some(42)
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
match some_number() {
|
||||
// Got `Some` variant, match if its value, bound to `n`,
|
||||
// is equal to 42.
|
||||
Some(n @ 42) => println!("The Answer: {}!", n),
|
||||
// Match any other number.
|
||||
Some(n) => println!("Not interesting... {}", n),
|
||||
// Match anything else (`None` variant).
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
part1();
|
||||
}
|
||||
|
48
src/bin/flow_control/match/destructuring/destructure_enum.rs
Normal file
48
src/bin/flow_control/match/destructuring/destructure_enum.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
// ./src/flow_control/match/destructuring/destructure_enum.md
|
||||
|
||||
|
||||
// `allow` required to silence warnings because only
|
||||
// one variant is used.
|
||||
#[allow(dead_code)]
|
||||
enum Color {
|
||||
// These 3 are specified solely by their name.
|
||||
Red,
|
||||
Blue,
|
||||
Green,
|
||||
// These likewise tie `u32` tuples to different names: color models.
|
||||
RGB(u32, u32, u32),
|
||||
HSV(u32, u32, u32),
|
||||
HSL(u32, u32, u32),
|
||||
CMY(u32, u32, u32),
|
||||
CMYK(u32, u32, u32, u32),
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
let color = Color::RGB(122, 17, 40);
|
||||
// TODO ^ Try different variants for `color`
|
||||
|
||||
println!("What color is it?");
|
||||
// An `enum` can be destructured using a `match`.
|
||||
match color {
|
||||
Color::Red => println!("The color is Red!"),
|
||||
Color::Blue => println!("The color is Blue!"),
|
||||
Color::Green => println!("The color is Green!"),
|
||||
Color::RGB(r, g, b) =>
|
||||
println!("Red: {}, green: {}, and blue: {}!", r, g, b),
|
||||
Color::HSV(h, s, v) =>
|
||||
println!("Hue: {}, saturation: {}, value: {}!", h, s, v),
|
||||
Color::HSL(h, s, l) =>
|
||||
println!("Hue: {}, saturation: {}, lightness: {}!", h, s, l),
|
||||
Color::CMY(c, m, y) =>
|
||||
println!("Cyan: {}, magenta: {}, yellow: {}!", c, m, y),
|
||||
Color::CMYK(c, m, y, k) =>
|
||||
println!("Cyan: {}, magenta: {}, yellow: {}, key (black): {}!",
|
||||
c, m, y, k),
|
||||
// Don't need another arm because all variants have been examined
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
// ./src/flow_control/match/destructuring/destructure_pointers.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
// Assign a reference of type `i32`. The `&` signifies there
|
||||
// is a reference being assigned.
|
||||
let reference = &4;
|
||||
|
||||
match reference {
|
||||
// If `reference` 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),
|
||||
}
|
||||
|
||||
// To avoid the `&`, you dereference before matching.
|
||||
match *reference {
|
||||
val => println!("Got a value via dereferencing: {:?}", val),
|
||||
}
|
||||
|
||||
// What if you don't start with a reference? `reference` was a `&`
|
||||
// because the right side was already a reference. This is not
|
||||
// a reference because the right side is not one.
|
||||
let _not_a_reference = 3;
|
||||
|
||||
// Rust provides `ref` for exactly this purpose. It modifies the
|
||||
// assignment so that a reference is created for the element; this
|
||||
// reference is assigned.
|
||||
let ref _is_a_reference = 3;
|
||||
|
||||
// Accordingly, by defining 2 values without references, references
|
||||
// can be retrieved via `ref` and `ref mut`.
|
||||
let value = 5;
|
||||
let mut mut_value = 6;
|
||||
|
||||
// Use `ref` keyword to create a reference.
|
||||
match value {
|
||||
ref r => println!("Got a reference to a value: {:?}", r),
|
||||
}
|
||||
|
||||
// Use `ref mut` similarly.
|
||||
match mut_value {
|
||||
ref mut m => {
|
||||
// Got a reference. Gotta dereference it before we can
|
||||
// add anything to it.
|
||||
*m += 10;
|
||||
println!("We added 10. `mut_value`: {:?}", m);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// ./src/flow_control/match/destructuring/destructure_slice.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
// Try changing the values in the array, or make it a slice!
|
||||
let array = [1, -2, 6];
|
||||
|
||||
match array {
|
||||
// Binds the second and the third elements to the respective variables
|
||||
[0, second, third] =>
|
||||
println!("array[0] = 0, array[1] = {}, array[2] = {}", second, third),
|
||||
|
||||
// Single values can be ignored with _
|
||||
[1, _, third] => println!(
|
||||
"array[0] = 1, array[2] = {} and array[1] was ignored",
|
||||
third
|
||||
),
|
||||
|
||||
// You can also bind some and ignore the rest
|
||||
[-1, second, ..] => println!(
|
||||
"array[0] = -1, array[1] = {} and all the other ones were ignored",
|
||||
second
|
||||
),
|
||||
// The code below would not compile
|
||||
// [-1, second] => ...
|
||||
|
||||
// Or store them in another array/slice (the type depends on
|
||||
// that of the value that is being matched against)
|
||||
[3, second, tail @ ..] => println!(
|
||||
"array[0] = 3, array[1] = {} and the other elements were {:?}",
|
||||
second, tail
|
||||
),
|
||||
|
||||
// Combining these patterns, we can, for example, bind the first and
|
||||
// last values, and store the rest of them in a single array
|
||||
[first, middle @ .., last] => println!(
|
||||
"array[0] = {}, middle = {:?}, array[2] = {}",
|
||||
first, middle, last
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// ./src/flow_control/match/destructuring/destructure_structures.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
struct Foo {
|
||||
x: (u32, u32),
|
||||
y: u32,
|
||||
}
|
||||
|
||||
// Try changing the values in the struct to see what happens
|
||||
let foo = Foo { x: (1, 2), y: 3 };
|
||||
|
||||
match foo {
|
||||
Foo { x: (1, b), y } => println!("First of x is 1, b = {}, y = {} ", b, y),
|
||||
|
||||
// you can destructure structs and rename the variables,
|
||||
// the order is not important
|
||||
Foo { y: 2, x: i } => println!("y is 2, i = {:?}", i),
|
||||
|
||||
// and you can also ignore some variables:
|
||||
Foo { y, .. } => println!("y = {}, we don't care about x", y),
|
||||
// this will give an error: pattern does not mention field `x`
|
||||
//Foo { y } => println!("y = {}", y),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// ./src/flow_control/match/destructuring/destructure_tuple.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
let triple = (0, -2, 3);
|
||||
// TODO ^ Try different values for `triple`
|
||||
|
||||
println!("Tell me about {:?}", triple);
|
||||
// Match can be used to destructure a tuple
|
||||
match triple {
|
||||
// Destructure the second and third elements
|
||||
(0, y, z) => println!("First is `0`, `y` is {:?}, and `z` is {:?}", y, z),
|
||||
(1, ..) => println!("First is `1` and the rest doesn't matter"),
|
||||
(.., 2) => println!("last is `2` and the rest doesn't matter"),
|
||||
(3, .., 4) => println!("First is `3`, last is `4`, and the rest doesn't matter"),
|
||||
// `..` can be used to ignore the rest of the tuple
|
||||
_ => println!("It doesn't matter what they are"),
|
||||
// `_` means don't bind the value to a variable
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
38
src/bin/flow_control/match/guard.rs
Normal file
38
src/bin/flow_control/match/guard.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
// ./src/flow_control/match/guard.md
|
||||
|
||||
|
||||
enum Temperature {
|
||||
Celsius(i32),
|
||||
Fahrenheit(i32),
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
let temperature = Temperature::Celsius(35);
|
||||
// ^ TODO try different values for `temperature`
|
||||
|
||||
match temperature {
|
||||
Temperature::Celsius(t) if t > 30 => println!("{}C is above 30 Celsius", t),
|
||||
// The `if condition` part ^ is a guard
|
||||
Temperature::Celsius(t) => println!("{}C is below 30 Celsius", t),
|
||||
|
||||
Temperature::Fahrenheit(t) if t > 86 => println!("{}F is above 86 Fahrenheit", t),
|
||||
Temperature::Fahrenheit(t) => println!("{}F is below 86 Fahrenheit", t),
|
||||
}
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
let number: u8 = 4;
|
||||
|
||||
match number {
|
||||
i if i == 0 => println!("Zero"),
|
||||
i if i > 0 => println!("Greater than zero"),
|
||||
// _ => unreachable!("Should never happen."),
|
||||
// TODO ^ uncomment to fix compilation
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
part1();
|
||||
}
|
||||
|
28
src/bin/flow_control/while.rs
Normal file
28
src/bin/flow_control/while.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
// ./src/flow_control/while.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
// A counter variable
|
||||
let mut n = 1;
|
||||
|
||||
// Loop while `n` is less than 101
|
||||
while n < 101 {
|
||||
if n % 15 == 0 {
|
||||
println!("fizzbuzz");
|
||||
} else if n % 3 == 0 {
|
||||
println!("fizz");
|
||||
} else if n % 5 == 0 {
|
||||
println!("buzz");
|
||||
} else {
|
||||
println!("{}", n);
|
||||
}
|
||||
|
||||
// Increment counter
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
28
src/bin/flow_control/while_let.rs
Normal file
28
src/bin/flow_control/while_let.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
// ./src/flow_control/while_let.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
// Make `optional` of type `Option<i32>`
|
||||
let mut optional = Some(0);
|
||||
|
||||
// This reads: "while `let` destructures `optional` into
|
||||
// `Some(i)`, evaluate the block (`{}`). Else `break`.
|
||||
while let Some(i) = optional {
|
||||
if i > 9 {
|
||||
println!("Greater than 9, quit!");
|
||||
optional = None;
|
||||
} else {
|
||||
println!("`i` is `{:?}`. Try again.", i);
|
||||
optional = Some(i + 1);
|
||||
}
|
||||
// ^ Less rightward drift and doesn't require
|
||||
// explicitly handling the failing case.
|
||||
}
|
||||
// ^ `if let` had additional optional `else`/`else if`
|
||||
// clauses. `while let` does not have these.
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
45
src/bin/fn.rs
Normal file
45
src/bin/fn.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
// ./src/fn.md
|
||||
|
||||
|
||||
// Unlike C/C++, there's no restriction on the order of function definitions
|
||||
fn part0() {
|
||||
// We can use this function here, and define it somewhere later
|
||||
fizzbuzz_to(100);
|
||||
}
|
||||
|
||||
// Function that returns a boolean value
|
||||
fn is_divisible_by(lhs: u32, rhs: u32) -> bool {
|
||||
// Corner case, early return
|
||||
if rhs == 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is an expression, the `return` keyword is not necessary here
|
||||
lhs % rhs == 0
|
||||
}
|
||||
|
||||
// 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 {
|
||||
fizzbuzz(n);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
34
src/bin/fn/closures.rs
Normal file
34
src/bin/fn/closures.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
// ./src/fn/closures.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
// Increment via closures and functions.
|
||||
fn function(i: i32) -> i32 { i + 1 }
|
||||
|
||||
// Closures are anonymous, here we are binding them to references
|
||||
// Annotation is identical to function annotation but is optional
|
||||
// as are the `{}` wrapping the body. These nameless functions
|
||||
// are assigned to appropriately named variables.
|
||||
let closure_annotated = |i: i32| -> i32 { i + 1 };
|
||||
let closure_inferred = |i | 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));
|
||||
// Once closure's type has been inferred, it cannot be inferred again with another type.
|
||||
//println!("cannot reuse closure_inferred with another type: {}", closure_inferred(42i64));
|
||||
// TODO: uncomment the line above and see the compiler error.
|
||||
|
||||
// A closure taking no arguments which returns an `i32`.
|
||||
// The return type is inferred.
|
||||
let one = || 1;
|
||||
println!("closure returning one: {}", one());
|
||||
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
25
src/bin/fn/closures/anonymity.rs
Normal file
25
src/bin/fn/closures/anonymity.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
// ./src/fn/closures/anonymity.md
|
||||
|
||||
|
||||
// `F` must implement `Fn` for a closure which takes no
|
||||
// inputs and returns nothing - exactly what is required
|
||||
// for `print`.
|
||||
fn apply<F>(f: F) where
|
||||
F: Fn() {
|
||||
f();
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
let x = 7;
|
||||
|
||||
// Capture `x` into an anonymous type and implement
|
||||
// `Fn` for it. Store it in `print`.
|
||||
let print = || println!("{}", x);
|
||||
|
||||
apply(print);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
96
src/bin/fn/closures/capture.rs
Normal file
96
src/bin/fn/closures/capture.rs
Normal file
|
@ -0,0 +1,96 @@
|
|||
// ./src/fn/closures/capture.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
use std::mem;
|
||||
|
||||
let color = String::from("green");
|
||||
|
||||
// A closure to print `color` which immediately borrows (`&`) `color` and
|
||||
// stores the borrow and closure in the `print` variable. It will remain
|
||||
// borrowed until `print` is used the last time.
|
||||
//
|
||||
// `println!` only requires arguments by immutable reference so it doesn't
|
||||
// impose anything more restrictive.
|
||||
let print = || println!("`color`: {}", color);
|
||||
|
||||
// Call the closure using the borrow.
|
||||
print();
|
||||
|
||||
// `color` can be borrowed immutably again, because the closure only holds
|
||||
// an immutable reference to `color`.
|
||||
let _reborrow = &color;
|
||||
print();
|
||||
|
||||
// A move or reborrow is allowed after the final use of `print`
|
||||
let _color_moved = color;
|
||||
|
||||
|
||||
let mut count = 0;
|
||||
// A closure to increment `count` could take either `&mut count` or `count`
|
||||
// but `&mut count` is less restrictive so it takes that. Immediately
|
||||
// borrows `count`.
|
||||
//
|
||||
// A `mut` is required on `inc` because a `&mut` is stored inside. Thus,
|
||||
// calling the closure mutates the closure which requires a `mut`.
|
||||
let mut inc = || {
|
||||
count += 1;
|
||||
println!("`count`: {}", count);
|
||||
};
|
||||
|
||||
// Call the closure using a mutable borrow.
|
||||
inc();
|
||||
|
||||
// The closure still mutably borrows `count` because it is called later.
|
||||
// An attempt to reborrow will lead to an error.
|
||||
// let _reborrow = &count;
|
||||
// ^ TODO: try uncommenting this line.
|
||||
inc();
|
||||
|
||||
// The closure no longer needs to borrow `&mut count`. Therefore, it is
|
||||
// possible to reborrow without an error
|
||||
let _count_reborrowed = &mut count;
|
||||
|
||||
|
||||
// A non-copy type.
|
||||
let movable = Box::new(3);
|
||||
|
||||
// `mem::drop` requires `T` so this must take by value. A copy type
|
||||
// would copy into the closure leaving the original untouched.
|
||||
// A non-copy must move and so `movable` immediately moves into
|
||||
// the closure.
|
||||
let consume = || {
|
||||
println!("`movable`: {:?}", movable);
|
||||
mem::drop(movable);
|
||||
};
|
||||
|
||||
// `consume` consumes the variable so this can only be called once.
|
||||
consume();
|
||||
// consume();
|
||||
// ^ TODO: Try uncommenting this line.
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
// `Vec` has non-copy semantics.
|
||||
let haystack = vec![1, 2, 3];
|
||||
|
||||
let contains = move |needle| haystack.contains(needle);
|
||||
|
||||
println!("{}", contains(&1));
|
||||
println!("{}", contains(&4));
|
||||
|
||||
// println!("There're {} elements in vec", haystack.len());
|
||||
// ^ Uncommenting above line will result in compile-time error
|
||||
// because borrow checker doesn't allow re-using variable after it
|
||||
// has been moved.
|
||||
|
||||
// Removing `move` from closure's signature will cause closure
|
||||
// to borrow _haystack_ variable immutably, hence _haystack_ is still
|
||||
// available and uncommenting above line will not cause an error.
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
part1();
|
||||
}
|
||||
|
33
src/bin/fn/closures/closure_examples/iter_any.rs
Normal file
33
src/bin/fn/closures/closure_examples/iter_any.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
// ./src/fn/closures/closure_examples/iter_any.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
let vec1 = vec![1, 2, 3];
|
||||
let vec2 = vec![4, 5, 6];
|
||||
|
||||
// `iter()` for vecs yields `&i32`. Destructure to `i32`.
|
||||
println!("2 in vec1: {}", vec1.iter() .any(|&x| x == 2));
|
||||
// `into_iter()` for vecs yields `i32`. No destructuring required.
|
||||
println!("2 in vec2: {}", vec2.into_iter().any(| x| x == 2));
|
||||
|
||||
// `iter()` only borrows `vec1` and its elements, so they can be used again
|
||||
println!("vec1 len: {}", vec1.len());
|
||||
println!("First element of vec1 is: {}", vec1[0]);
|
||||
// `into_iter()` does move `vec2` and its elements, so they cannot be used again
|
||||
// println!("First element of vec2 is: {}", vec2[0]);
|
||||
// println!("vec2 len: {}", vec2.len());
|
||||
// TODO: uncomment two lines above and see compiler errors.
|
||||
|
||||
let array1 = [1, 2, 3];
|
||||
let array2 = [4, 5, 6];
|
||||
|
||||
// `iter()` for arrays yields `&i32`.
|
||||
println!("2 in array1: {}", array1.iter() .any(|&x| x == 2));
|
||||
// `into_iter()` for arrays yields `i32`.
|
||||
println!("2 in array2: {}", array2.into_iter().any(|x| x == 2));
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
47
src/bin/fn/closures/closure_examples/iter_find.rs
Normal file
47
src/bin/fn/closures/closure_examples/iter_find.rs
Normal file
|
@ -0,0 +1,47 @@
|
|||
// ./src/fn/closures/closure_examples/iter_find.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
let vec1 = vec![1, 2, 3];
|
||||
let vec2 = vec![4, 5, 6];
|
||||
|
||||
// `iter()` for vecs yields `&i32`.
|
||||
let mut iter = vec1.iter();
|
||||
// `into_iter()` for vecs yields `i32`.
|
||||
let mut into_iter = vec2.into_iter();
|
||||
|
||||
// `iter()` for vecs yields `&i32`, and we want to reference one of its
|
||||
// items, so we have to destructure `&&i32` to `i32`
|
||||
println!("Find 2 in vec1: {:?}", iter .find(|&&x| x == 2));
|
||||
// `into_iter()` for vecs yields `i32`, and we want to reference one of
|
||||
// its items, so we have to destructure `&i32` to `i32`
|
||||
println!("Find 2 in vec2: {:?}", into_iter.find(| &x| x == 2));
|
||||
|
||||
let array1 = [1, 2, 3];
|
||||
let array2 = [4, 5, 6];
|
||||
|
||||
// `iter()` for arrays yields `&i32`
|
||||
println!("Find 2 in array1: {:?}", array1.iter() .find(|&&x| x == 2));
|
||||
// `into_iter()` for arrays yields `i32`
|
||||
println!("Find 2 in array2: {:?}", array2.into_iter().find(|&x| x == 2));
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
let vec = vec![1, 9, 3, 3, 13, 2];
|
||||
|
||||
// `iter()` for vecs yields `&i32` and `position()` does not take a reference, so
|
||||
// we have to destructure `&i32` to `i32`
|
||||
let index_of_first_even_number = vec.iter().position(|&x| x % 2 == 0);
|
||||
assert_eq!(index_of_first_even_number, Some(5));
|
||||
|
||||
// `into_iter()` for vecs yields `i32` and `position()` does not take a reference, so
|
||||
// we do not have to destructure
|
||||
let index_of_first_negative_number = vec.into_iter().position(|x| x < 0);
|
||||
assert_eq!(index_of_first_negative_number, None);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
part1();
|
||||
}
|
||||
|
26
src/bin/fn/closures/input_functions.rs
Normal file
26
src/bin/fn/closures/input_functions.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
// ./src/fn/closures/input_functions.md
|
||||
|
||||
|
||||
// Define a function which takes a generic `F` argument
|
||||
// bounded by `Fn`, and calls it
|
||||
fn call_me<F: Fn()>(f: F) {
|
||||
f();
|
||||
}
|
||||
|
||||
// Define a wrapper function satisfying the `Fn` bound
|
||||
fn function() {
|
||||
println!("I'm a function!");
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
// Define a closure satisfying the `Fn` bound
|
||||
let closure = || println!("I'm a closure!");
|
||||
|
||||
call_me(closure);
|
||||
call_me(function);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
59
src/bin/fn/closures/input_parameters.rs
Normal file
59
src/bin/fn/closures/input_parameters.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
// ./src/fn/closures/input_parameters.md
|
||||
|
||||
|
||||
// A function which takes a closure as an argument and calls it.
|
||||
// <F> denotes that F is a "Generic type parameter"
|
||||
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();
|
||||
}
|
||||
|
||||
// A function which takes a closure and returns an `i32`.
|
||||
fn apply_to_3<F>(f: F) -> i32 where
|
||||
// The closure takes an `i32` and returns an `i32`.
|
||||
F: Fn(i32) -> i32 {
|
||||
|
||||
f(3)
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
use std::mem;
|
||||
|
||||
let greeting = "hello";
|
||||
// A non-copy type.
|
||||
// `to_owned` creates owned data from borrowed one
|
||||
let mut farewell = "goodbye".to_owned();
|
||||
|
||||
// Capture 2 variables: `greeting` by reference and
|
||||
// `farewell` by value.
|
||||
let diary = || {
|
||||
// `greeting` is by reference: requires `Fn`.
|
||||
println!("I said {}.", greeting);
|
||||
|
||||
// Mutation forces `farewell` to be captured by
|
||||
// mutable reference. Now requires `FnMut`.
|
||||
farewell.push_str("!!!");
|
||||
println!("Then I screamed {}.", farewell);
|
||||
println!("Now I can sleep. zzzzz");
|
||||
|
||||
// Manually calling drop forces `farewell` to
|
||||
// be captured by value. Now requires `FnOnce`.
|
||||
mem::drop(farewell);
|
||||
};
|
||||
|
||||
// Call the function which applies the closure.
|
||||
apply(diary);
|
||||
|
||||
// `double` satisfies `apply_to_3`'s trait bound
|
||||
let double = |x| 2 * x;
|
||||
|
||||
println!("3 doubled: {}", apply_to_3(double));
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
35
src/bin/fn/closures/output_parameters.rs
Normal file
35
src/bin/fn/closures/output_parameters.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
// ./src/fn/closures/output_parameters.md
|
||||
|
||||
|
||||
fn create_fn() -> impl Fn() {
|
||||
let text = "Fn".to_owned();
|
||||
|
||||
move || println!("This is a: {}", text)
|
||||
}
|
||||
|
||||
fn create_fnmut() -> impl FnMut() {
|
||||
let text = "FnMut".to_owned();
|
||||
|
||||
move || println!("This is a: {}", text)
|
||||
}
|
||||
|
||||
fn create_fnonce() -> impl FnOnce() {
|
||||
let text = "FnOnce".to_owned();
|
||||
|
||||
move || println!("This is a: {}", text)
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
let fn_plain = create_fn();
|
||||
let mut fn_mut = create_fnmut();
|
||||
let fn_once = create_fnonce();
|
||||
|
||||
fn_plain();
|
||||
fn_mut();
|
||||
fn_once();
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
42
src/bin/fn/hof.rs
Normal file
42
src/bin/fn/hof.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
// ./src/fn/hof.md
|
||||
|
||||
|
||||
fn is_odd(n: u32) -> bool {
|
||||
n % 2 == 1
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
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;
|
||||
|
||||
if n_squared >= upper {
|
||||
// Break loop if exceeded the upper limit
|
||||
break;
|
||||
} else if is_odd(n_squared) {
|
||||
// Accumulate value, if it's odd
|
||||
acc += n_squared;
|
||||
}
|
||||
}
|
||||
println!("imperative style: {}", acc);
|
||||
|
||||
// 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
|
||||
.sum(); // Sum them
|
||||
println!("functional style: {}", sum_of_squared_odd_numbers);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
117
src/bin/fn/methods.rs
Normal file
117
src/bin/fn/methods.rs
Normal file
|
@ -0,0 +1,117 @@
|
|||
// ./src/fn/methods.md
|
||||
|
||||
|
||||
struct Point {
|
||||
x: f64,
|
||||
y: f64,
|
||||
}
|
||||
|
||||
// Implementation block, all `Point` associated functions & methods go in here
|
||||
impl Point {
|
||||
// This is an "associated function" because this function is associated with
|
||||
// a particular type, that is, Point.
|
||||
//
|
||||
// Associated functions don't need to be called with an instance.
|
||||
// These functions are generally used like constructors.
|
||||
fn origin() -> Point {
|
||||
Point { x: 0.0, y: 0.0 }
|
||||
}
|
||||
|
||||
// Another associated function, taking two arguments:
|
||||
fn new(x: f64, y: f64) -> Point {
|
||||
Point { x: x, y: y }
|
||||
}
|
||||
}
|
||||
|
||||
struct Rectangle {
|
||||
p1: Point,
|
||||
p2: Point,
|
||||
}
|
||||
|
||||
impl Rectangle {
|
||||
// This is a 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;
|
||||
|
||||
// `abs` is a `f64` method that returns the absolute value of the
|
||||
// caller
|
||||
((x1 - x2) * (y1 - y2)).abs()
|
||||
}
|
||||
|
||||
fn perimeter(&self) -> f64 {
|
||||
let Point { x: x1, y: y1 } = self.p1;
|
||||
let Point { x: x2, y: y2 } = self.p2;
|
||||
|
||||
2.0 * ((x1 - x2).abs() + (y1 - y2).abs())
|
||||
}
|
||||
|
||||
// This method requires the caller object to be mutable
|
||||
// `&mut self` desugars to `self: &mut Self`
|
||||
fn translate(&mut self, x: f64, y: f64) {
|
||||
self.p1.x += x;
|
||||
self.p2.x += x;
|
||||
|
||||
self.p1.y += y;
|
||||
self.p2.y += y;
|
||||
}
|
||||
}
|
||||
|
||||
// `Pair` owns resources: two heap allocated integers
|
||||
struct Pair(Box<i32>, Box<i32>);
|
||||
|
||||
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;
|
||||
|
||||
println!("Destroying Pair({}, {})", first, second);
|
||||
|
||||
// `first` and `second` go out of scope and get freed
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
let rectangle = Rectangle {
|
||||
// Associated functions are called using double colons
|
||||
p1: Point::origin(),
|
||||
p2: Point::new(3.0, 4.0),
|
||||
};
|
||||
|
||||
// Methods are called using the dot operator
|
||||
// Note that the first argument `&self` is implicitly passed, i.e.
|
||||
// `rectangle.perimeter()` === `Rectangle::perimeter(&rectangle)`
|
||||
println!("Rectangle perimeter: {}", rectangle.perimeter());
|
||||
println!("Rectangle area: {}", rectangle.area());
|
||||
|
||||
let mut square = Rectangle {
|
||||
p1: Point::origin(),
|
||||
p2: Point::new(1.0, 1.0),
|
||||
};
|
||||
|
||||
// Error! `rectangle` is immutable, but this method requires a mutable
|
||||
// object
|
||||
//rectangle.translate(1.0, 0.0);
|
||||
// TODO ^ Try uncommenting this line
|
||||
|
||||
// Okay! Mutable objects can call mutable methods
|
||||
square.translate(1.0, 1.0);
|
||||
|
||||
let pair = Pair(Box::new(1), Box::new(2));
|
||||
|
||||
pair.destroy();
|
||||
|
||||
// Error! Previous `destroy` call "consumed" `pair`
|
||||
//pair.destroy();
|
||||
// TODO ^ Try uncommenting this line
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
35
src/bin/generics.rs
Normal file
35
src/bin/generics.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
// ./src/generics.md
|
||||
|
||||
|
||||
// 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 part0() {
|
||||
// `Single` is concrete and explicitly takes `A`.
|
||||
let _s = Single(A);
|
||||
|
||||
// Create a variable `_char` of type `SingleGen<char>`
|
||||
// and give it the value `SingleGen('a')`.
|
||||
// Here, `SingleGen` has a type parameter explicitly specified.
|
||||
let _char: SingleGen<char> = SingleGen('a');
|
||||
|
||||
// `SingleGen` can also have a type parameter implicitly specified:
|
||||
let _t = SingleGen(A); // Uses `A` defined at the top.
|
||||
let _i32 = SingleGen(6); // Uses `i32`.
|
||||
let _char = SingleGen('a'); // Uses `char`.
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
52
src/bin/generics/assoc_items/the_problem.rs
Normal file
52
src/bin/generics/assoc_items/the_problem.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
// ./src/generics/assoc_items/the_problem.md
|
||||
|
||||
|
||||
struct Container(i32, i32);
|
||||
|
||||
// A trait which checks if 2 items are stored inside of container.
|
||||
// Also retrieves first or last value.
|
||||
trait Contains<A, B> {
|
||||
fn contains(&self, _: &A, _: &B) -> bool; // Explicitly requires `A` and `B`.
|
||||
fn first(&self) -> i32; // Doesn't explicitly require `A` or `B`.
|
||||
fn last(&self) -> i32; // Doesn't explicitly require `A` or `B`.
|
||||
}
|
||||
|
||||
impl Contains<i32, i32> for Container {
|
||||
// True if the numbers stored are equal.
|
||||
fn contains(&self, number_1: &i32, number_2: &i32) -> bool {
|
||||
(&self.0 == number_1) && (&self.1 == number_2)
|
||||
}
|
||||
|
||||
// Grab the first number.
|
||||
fn first(&self) -> i32 { self.0 }
|
||||
|
||||
// Grab the last number.
|
||||
fn last(&self) -> i32 { self.1 }
|
||||
}
|
||||
|
||||
// `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 part0() {
|
||||
let number_1 = 3;
|
||||
let number_2 = 10;
|
||||
|
||||
let container = Container(number_1, number_2);
|
||||
|
||||
println!("Does container contain {} and {}: {}",
|
||||
&number_1, &number_2,
|
||||
container.contains(&number_1, &number_2));
|
||||
println!("First number: {}", container.first());
|
||||
println!("Last number: {}", container.last());
|
||||
|
||||
println!("The difference is: {}", difference(&container));
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
58
src/bin/generics/assoc_items/types.rs
Normal file
58
src/bin/generics/assoc_items/types.rs
Normal file
|
@ -0,0 +1,58 @@
|
|||
// ./src/generics/assoc_items/types.md
|
||||
|
||||
|
||||
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 contains(&self, _: &Self::A, _: &Self::B) -> bool;
|
||||
fn first(&self) -> i32;
|
||||
fn last(&self) -> i32;
|
||||
}
|
||||
|
||||
impl Contains for Container {
|
||||
// Specify what types `A` and `B` are. If the `input` type
|
||||
// is `Container(i32, i32)`, the `output` types are determined
|
||||
// as `i32` and `i32`.
|
||||
type A = i32;
|
||||
type B = i32;
|
||||
|
||||
// `&Self::A` and `&Self::B` are also valid here.
|
||||
fn contains(&self, number_1: &i32, number_2: &i32) -> bool {
|
||||
(&self.0 == number_1) && (&self.1 == number_2)
|
||||
}
|
||||
// Grab the first number.
|
||||
fn first(&self) -> i32 { self.0 }
|
||||
|
||||
// Grab the last number.
|
||||
fn last(&self) -> i32 { self.1 }
|
||||
}
|
||||
|
||||
fn difference<C: Contains>(container: &C) -> i32 {
|
||||
container.last() - container.first()
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
let number_1 = 3;
|
||||
let number_2 = 10;
|
||||
|
||||
let container = Container(number_1, number_2);
|
||||
|
||||
println!("Does container contain {} and {}: {}",
|
||||
&number_1, &number_2,
|
||||
container.contains(&number_1, &number_2));
|
||||
println!("First number: {}", container.first());
|
||||
println!("Last number: {}", container.last());
|
||||
|
||||
println!("The difference is: {}", difference(&container));
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
46
src/bin/generics/bounds.rs
Normal file
46
src/bin/generics/bounds.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
// ./src/generics/bounds.md
|
||||
|
||||
|
||||
// A trait which implements the print marker: `{:?}`.
|
||||
use std::fmt::Debug;
|
||||
|
||||
trait HasArea {
|
||||
fn area(&self) -> f64;
|
||||
}
|
||||
|
||||
impl HasArea for Rectangle {
|
||||
fn area(&self) -> f64 { self.length * self.height }
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Rectangle { length: f64, height: f64 }
|
||||
#[allow(dead_code)]
|
||||
struct Triangle { length: f64, height: f64 }
|
||||
|
||||
// The generic `T` must implement `Debug`. Regardless
|
||||
// of the type, this will work properly.
|
||||
fn print_debug<T: Debug>(t: &T) {
|
||||
println!("{:?}", t);
|
||||
}
|
||||
|
||||
// `T` must implement `HasArea`. Any type which meets
|
||||
// the bound can access `HasArea`'s function `area`.
|
||||
fn area<T: HasArea>(t: &T) -> f64 { t.area() }
|
||||
|
||||
fn part0() {
|
||||
let rectangle = Rectangle { length: 3.0, height: 4.0 };
|
||||
let _triangle = Triangle { length: 3.0, height: 4.0 };
|
||||
|
||||
print_debug(&rectangle);
|
||||
println!("Area: {}", rectangle.area());
|
||||
|
||||
//print_debug(&_triangle);
|
||||
//println!("Area: {}", _triangle.area());
|
||||
// ^ TODO: Try uncommenting these.
|
||||
// | Error: Does not implement either `Debug` or `HasArea`.
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
35
src/bin/generics/bounds/testcase_empty.rs
Normal file
35
src/bin/generics/bounds/testcase_empty.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
// ./src/generics/bounds/testcase_empty.md
|
||||
|
||||
|
||||
struct Cardinal;
|
||||
struct BlueJay;
|
||||
struct Turkey;
|
||||
|
||||
trait Red {}
|
||||
trait Blue {}
|
||||
|
||||
impl Red for Cardinal {}
|
||||
impl Blue for BlueJay {}
|
||||
|
||||
// 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 part0() {
|
||||
let cardinal = Cardinal;
|
||||
let blue_jay = BlueJay;
|
||||
let _turkey = Turkey;
|
||||
|
||||
// `red()` won't work on a blue jay nor vice versa
|
||||
// because of the bounds.
|
||||
println!("A cardinal is {}", red(&cardinal));
|
||||
println!("A blue jay is {}", blue(&blue_jay));
|
||||
//println!("A turkey is {}", red(&_turkey));
|
||||
// ^ TODO: Try uncommenting this line.
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
45
src/bin/generics/gen_fn.rs
Normal file
45
src/bin/generics/gen_fn.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
// ./src/generics/gen_fn.md
|
||||
|
||||
|
||||
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.
|
||||
|
||||
// Define a function `reg_fn` that takes an argument `_s` of type `S`.
|
||||
// This has no `<T>` so this is not a generic function.
|
||||
fn reg_fn(_s: S) {}
|
||||
|
||||
// Define a function `gen_spec_t` that takes an argument `_s` of type `SGen<T>`.
|
||||
// It has been explicitly given the type parameter `A`, but because `A` has not
|
||||
// been specified as a generic type parameter for `gen_spec_t`, it is not generic.
|
||||
fn gen_spec_t(_s: SGen<A>) {}
|
||||
|
||||
// Define a function `gen_spec_i32` that takes an argument `_s` of type `SGen<i32>`.
|
||||
// It has been explicitly given the type parameter `i32`, which is a specific type.
|
||||
// Because `i32` is not a generic type, this function is also not generic.
|
||||
fn gen_spec_i32(_s: SGen<i32>) {}
|
||||
|
||||
// Define a function `generic` that takes an argument `_s` of type `SGen<T>`.
|
||||
// Because `SGen<T>` is preceded by `<T>`, this function is generic over `T`.
|
||||
fn generic<T>(_s: SGen<T>) {}
|
||||
|
||||
fn part0() {
|
||||
// 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`.
|
||||
|
||||
// Explicitly specified type parameter `char` to `generic()`.
|
||||
generic::<char>(SGen('a'));
|
||||
|
||||
// Implicitly specified type parameter `char` to `generic()`.
|
||||
generic(SGen('c'));
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
38
src/bin/generics/gen_trait.rs
Normal file
38
src/bin/generics/gen_trait.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
// ./src/generics/gen_trait.md
|
||||
|
||||
|
||||
// Non-copyable types.
|
||||
struct Empty;
|
||||
struct Null;
|
||||
|
||||
// A trait generic over `T`.
|
||||
trait DoubleDrop<T> {
|
||||
// Define a method on the caller type which takes an
|
||||
// additional single parameter `T` and does nothing with it.
|
||||
fn double_drop(self, _: T);
|
||||
}
|
||||
|
||||
// Implement `DoubleDrop<T>` for any generic parameter `T` and
|
||||
// caller `U`.
|
||||
impl<T, U> DoubleDrop<T> for U {
|
||||
// This method takes ownership of both passed arguments,
|
||||
// deallocating both.
|
||||
fn double_drop(self, _: T) {}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
let empty = Empty;
|
||||
let null = Null;
|
||||
|
||||
// Deallocate `empty` and `null`.
|
||||
empty.double_drop(null);
|
||||
|
||||
//empty;
|
||||
//null;
|
||||
// ^ TODO: Try uncommenting these lines.
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
36
src/bin/generics/impl.rs
Normal file
36
src/bin/generics/impl.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
// ./src/generics/impl.md
|
||||
|
||||
|
||||
struct Val {
|
||||
val: f64,
|
||||
}
|
||||
|
||||
struct GenVal<T> {
|
||||
gen_val: T,
|
||||
}
|
||||
|
||||
// impl of Val
|
||||
impl Val {
|
||||
fn value(&self) -> &f64 {
|
||||
&self.val
|
||||
}
|
||||
}
|
||||
|
||||
// impl of GenVal for a generic type `T`
|
||||
impl<T> GenVal<T> {
|
||||
fn value(&self) -> &T {
|
||||
&self.gen_val
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
let x = Val { val: 3.0 };
|
||||
let y = GenVal { gen_val: 3i32 };
|
||||
|
||||
println!("{}, {}", x.value(), y.value());
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
31
src/bin/generics/multi_bounds.rs
Normal file
31
src/bin/generics/multi_bounds.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
// ./src/generics/multi_bounds.md
|
||||
|
||||
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
fn compare_prints<T: Debug + Display>(t: &T) {
|
||||
println!("Debug: `{:?}`", t);
|
||||
println!("Display: `{}`", t);
|
||||
}
|
||||
|
||||
fn compare_types<T: Debug, U: Debug>(t: &T, u: &U) {
|
||||
println!("t: `{:?}`", t);
|
||||
println!("u: `{:?}`", u);
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
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);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
47
src/bin/generics/phantom.rs
Normal file
47
src/bin/generics/phantom.rs
Normal file
|
@ -0,0 +1,47 @@
|
|||
// ./src/generics/phantom.md
|
||||
|
||||
|
||||
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 part0() {
|
||||
// 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);
|
||||
|
||||
// Type specified as `<char, f32>`.
|
||||
let _struct1: PhantomStruct<char, f32> = PhantomStruct {
|
||||
first: 'Q',
|
||||
phantom: PhantomData,
|
||||
};
|
||||
// Type specified as `<char, f64>`.
|
||||
let _struct2: PhantomStruct<char, f64> = PhantomStruct {
|
||||
first: 'Q',
|
||||
phantom: PhantomData,
|
||||
};
|
||||
|
||||
// Compile-time Error! Type mismatch so these cannot be compared:
|
||||
// println!("_tuple1 == _tuple2 yields: {}",
|
||||
// _tuple1 == _tuple2);
|
||||
|
||||
// Compile-time Error! Type mismatch so these cannot be compared:
|
||||
// println!("_struct1 == _struct2 yields: {}",
|
||||
// _struct1 == _struct2);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
56
src/bin/generics/phantom/testcase_units.rs
Normal file
56
src/bin/generics/phantom/testcase_units.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
// ./src/generics/phantom/testcase_units.md
|
||||
|
||||
|
||||
use std::ops::Add;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// Create void enumerations to define unit types.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Inch {}
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Mm {}
|
||||
|
||||
/// `Length` is a type with phantom type parameter `Unit`,
|
||||
/// and is not generic over the length type (that is `f64`).
|
||||
///
|
||||
/// `f64` already implements the `Clone` and `Copy` traits.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Length<Unit>(f64, PhantomData<Unit>);
|
||||
|
||||
/// The `Add` trait defines the behavior of the `+` operator.
|
||||
impl<Unit> Add for Length<Unit> {
|
||||
type Output = Length<Unit>;
|
||||
|
||||
// add() returns a new `Length` struct containing the sum.
|
||||
fn add(self, rhs: Length<Unit>) -> Length<Unit> {
|
||||
// `+` calls the `Add` implementation for `f64`.
|
||||
Length(self.0 + rhs.0, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
// 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);
|
||||
|
||||
// `+` calls the `add()` method we implemented for `Length<Unit>`.
|
||||
//
|
||||
// Since `Length` implements `Copy`, `add()` does not consume
|
||||
// `one_foot` and `one_meter` but copies them into `self` and `rhs`.
|
||||
let two_feet = one_foot + one_foot;
|
||||
let two_meters = one_meter + one_meter;
|
||||
|
||||
// Addition works.
|
||||
println!("one foot + one_foot = {:?} in", two_feet.0);
|
||||
println!("one meter + one_meter = {:?} mm", two_meters.0);
|
||||
|
||||
// Nonsensical operations fail as they should:
|
||||
// Compile-time Error: type mismatch.
|
||||
//let one_feter = one_foot + one_meter;
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
30
src/bin/generics/where.rs
Normal file
30
src/bin/generics/where.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
// ./src/generics/where.md
|
||||
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
trait PrintInOption {
|
||||
fn print_in_option(self);
|
||||
}
|
||||
|
||||
// Because we would otherwise have to express this as `T: Debug` or
|
||||
// use another method of indirect approach, this requires a `where` clause:
|
||||
impl<T> PrintInOption for T where
|
||||
Option<T>: Debug {
|
||||
// We want `Option<T>: Debug` as our bound because that is what's
|
||||
// being printed. Doing otherwise would be using the wrong bound.
|
||||
fn print_in_option(self) {
|
||||
println!("{:?}", Some(self));
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
let vec = vec![1, 2, 3];
|
||||
|
||||
vec.print_in_option();
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
// ./src/hello.md
|
||||
|
||||
|
||||
// This is a comment, and is ignored by the compiler
|
||||
// You can test this code by clicking the "Run" button over there ->
|
||||
// or if you prefer to use your keyboard, you can use the "Ctrl + Enter" shortcut
|
||||
|
@ -6,10 +9,14 @@
|
|||
// You can always return to the original code by clicking the "Reset" button ->
|
||||
|
||||
// This is the main function
|
||||
fn main() {
|
||||
fn part0() {
|
||||
// Statements here are executed when the compiled binary is called
|
||||
|
||||
// Print text to the console
|
||||
println!("Hello World! This is an APE built with Rust.");
|
||||
println!("Hello World!");
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
38
src/bin/hello/comment.rs
Normal file
38
src/bin/hello/comment.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
// ./src/hello/comment.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
// This is an example of a line comment
|
||||
// There are two slashes at the beginning of the line
|
||||
// And 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, a block comment. In general,
|
||||
* line comments are the recommended comment style. But
|
||||
* block comments are extremely useful for temporarily disabling
|
||||
* chunks of code. /* Block comments can be /* nested, */ */
|
||||
* so it takes only a few keystrokes to comment out everything
|
||||
* in this part0() function. /*/*/* Try it yourself! */*/*/
|
||||
*/
|
||||
|
||||
/*
|
||||
Note: The previous column of `*` was entirely for style. There's
|
||||
no actual need for it.
|
||||
*/
|
||||
|
||||
// You can manipulate expressions more easily with block comments
|
||||
// than with line comments. Try deleting the comment delimiters
|
||||
// to change the result:
|
||||
let x = 5 + /* 90 + */ 5;
|
||||
println!("Is `x` 10 or 100? x = {}", x);
|
||||
}
|
||||
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
68
src/bin/hello/print.rs
Normal file
68
src/bin/hello/print.rs
Normal file
|
@ -0,0 +1,68 @@
|
|||
// ./src/hello/print.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
// In general, the `{}` will be automatically replaced with any
|
||||
// arguments. These will be stringified.
|
||||
println!("{} days", 31);
|
||||
|
||||
// Positional arguments can be used. Specifying an integer inside `{}`
|
||||
// determines which additional argument will be replaced. Arguments start
|
||||
// at 0 immediately after the format string
|
||||
println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob");
|
||||
|
||||
// As can named arguments.
|
||||
println!("{subject} {verb} {object}",
|
||||
object="the lazy dog",
|
||||
subject="the quick brown fox",
|
||||
verb="jumps over");
|
||||
|
||||
// Different formatting can be invoked by specifying the format character after a
|
||||
// `:`.
|
||||
println!("Base 10: {}", 69420); //69420
|
||||
println!("Base 2 (binary): {:b}", 69420); //10000111100101100
|
||||
println!("Base 8 (octal): {:o}", 69420); //207454
|
||||
println!("Base 16 (hexadecimal): {:x}", 69420); //10f2c
|
||||
println!("Base 16 (hexadecimal): {:X}", 69420); //10F2C
|
||||
|
||||
|
||||
// You can right-justify text with a specified width. This will
|
||||
// output " 1". (Four white spaces and a "1", for a total width of 5.)
|
||||
println!("{number:>5}", number=1);
|
||||
|
||||
// You can pad numbers with extra zeroes,
|
||||
//and left-adjust by flipping the sign. This will output "10000".
|
||||
println!("{number:0<5}", number=1);
|
||||
|
||||
// You can use named arguments in the format specifier by appending a `$`
|
||||
println!("{number:0>width$}", number=1, width=5);
|
||||
|
||||
|
||||
// Rust even checks to make sure the correct number of arguments are
|
||||
// used.
|
||||
println!("My name is {0}, {1} {0}", "Bond");
|
||||
// FIXME ^ Add the missing argument: "James"
|
||||
|
||||
// Only types that implement fmt::Display can be formatted with `{}`. User-
|
||||
// defined types do not implement fmt::Display by default
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Structure(i32);
|
||||
|
||||
// This will not compile because `Structure` does not implement
|
||||
// fmt::Display
|
||||
//println!("This struct `{}` won't print...", Structure(3));
|
||||
// TODO ^ Try uncommenting this line
|
||||
|
||||
// For Rust 1.58 and above, you can directly capture the argument from a
|
||||
// surrounding variable. Just like the above, this will output
|
||||
// " 1". 5 white spaces and a "1".
|
||||
let number: f64 = 1.0;
|
||||
let width: usize = 5;
|
||||
println!("{number:>width$}");
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
56
src/bin/hello/print/fmt.rs
Normal file
56
src/bin/hello/print/fmt.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
// ./src/hello/print/fmt.md
|
||||
|
||||
|
||||
use std::fmt::{self, Formatter, Display};
|
||||
|
||||
struct City {
|
||||
name: &'static str,
|
||||
// Latitude
|
||||
lat: f32,
|
||||
// Longitude
|
||||
lon: f32,
|
||||
}
|
||||
|
||||
impl Display for City {
|
||||
// `f` is a buffer, and this method must write the formatted string into it
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
let lat_c = if self.lat >= 0.0 { 'N' } else { 'S' };
|
||||
let lon_c = if self.lon >= 0.0 { 'E' } else { 'W' };
|
||||
|
||||
// `write!` is like `format!`, but it will write the formatted string
|
||||
// into a buffer (the first argument)
|
||||
write!(f, "{}: {:.3}°{} {:.3}°{}",
|
||||
self.name, self.lat.abs(), lat_c, self.lon.abs(), lon_c)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Color {
|
||||
red: u8,
|
||||
green: u8,
|
||||
blue: u8,
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
49
src/bin/hello/print/print_debug.rs
Normal file
49
src/bin/hello/print/print_debug.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
// ./src/hello/print/print_debug.md
|
||||
|
||||
|
||||
// Derive the `fmt::Debug` implementation for `Structure`. `Structure`
|
||||
// is a structure which contains a single `i32`.
|
||||
#[derive(Debug)]
|
||||
struct Structure(i32);
|
||||
|
||||
// Put a `Structure` inside of the structure `Deep`. Make it printable
|
||||
// also.
|
||||
#[derive(Debug)]
|
||||
struct Deep(Structure);
|
||||
|
||||
fn part0() {
|
||||
// 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));
|
||||
|
||||
// The problem with `derive` is there is no control over how
|
||||
// the results look. What if I want this to just show a `7`?
|
||||
println!("Now {:?} will print!", Deep(Structure(7)));
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Person<'a> {
|
||||
name: &'a str,
|
||||
age: u8
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
let name = "Peter";
|
||||
let age = 27;
|
||||
let peter = Person { name, age };
|
||||
|
||||
// Pretty print
|
||||
println!("{:#?}", peter);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
part1();
|
||||
}
|
||||
|
62
src/bin/hello/print/print_display.rs
Normal file
62
src/bin/hello/print/print_display.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
// ./src/hello/print/print_display.md
|
||||
|
||||
|
||||
use std::fmt; // Import `fmt`
|
||||
|
||||
// A structure holding two numbers. `Debug` will be derived so the results can
|
||||
// be contrasted with `Display`.
|
||||
#[derive(Debug)]
|
||||
struct MinMax(i64, i64);
|
||||
|
||||
// Implement `Display` for `MinMax`.
|
||||
impl fmt::Display for MinMax {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// Use `self.number` to refer to each positional data point.
|
||||
write!(f, "({}, {})", self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
// Define a structure where the fields are nameable for comparison.
|
||||
#[derive(Debug)]
|
||||
struct Point2D {
|
||||
x: f64,
|
||||
y: f64,
|
||||
}
|
||||
|
||||
// Similarly, implement `Display` for `Point2D`
|
||||
impl fmt::Display for Point2D {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// Customize so only `x` and `y` are denoted.
|
||||
write!(f, "x: {}, y: {}", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
let minmax = MinMax(0, 14);
|
||||
|
||||
println!("Compare structures:");
|
||||
println!("Display: {}", minmax);
|
||||
println!("Debug: {:?}", minmax);
|
||||
|
||||
let big_range = MinMax(-300, 300);
|
||||
let small_range = MinMax(-3, 3);
|
||||
|
||||
println!("The big range is {big} and the small is {small}",
|
||||
small = small_range,
|
||||
big = big_range);
|
||||
|
||||
let point = Point2D { x: 3.3, y: 7.2 };
|
||||
|
||||
println!("Compare points:");
|
||||
println!("Display: {}", point);
|
||||
println!("Debug: {:?}", point);
|
||||
|
||||
// Error. Both `Debug` and `Display` were implemented, but `{:b}`
|
||||
// requires `fmt::Binary` to be implemented. This will not work.
|
||||
// println!("What does Point2D look like in binary: {:b}?", point);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
39
src/bin/hello/print/print_display/testcase_list.rs
Normal file
39
src/bin/hello/print/print_display/testcase_list.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
// ./src/hello/print/print_display/testcase_list.md
|
||||
|
||||
|
||||
use std::fmt; // Import the `fmt` module.
|
||||
|
||||
// Define a structure named `List` containing a `Vec`.
|
||||
struct List(Vec<i32>);
|
||||
|
||||
impl fmt::Display for List {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// Extract the value using tuple indexing,
|
||||
// and create a reference to `vec`.
|
||||
let vec = &self.0;
|
||||
|
||||
write!(f, "[")?;
|
||||
|
||||
// Iterate over `v` in `vec` while enumerating the iteration
|
||||
// count in `count`.
|
||||
for (count, v) in vec.iter().enumerate() {
|
||||
// For every element except the first, add a comma.
|
||||
// Use the ? operator to return on errors.
|
||||
if count != 0 { write!(f, ", ")?; }
|
||||
write!(f, "{}", v)?;
|
||||
}
|
||||
|
||||
// Close the opened bracket and return a fmt::Result value.
|
||||
write!(f, "]")
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
let v = List(vec![1, 2, 3]);
|
||||
println!("{}", v);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
21
src/bin/macros.rs
Normal file
21
src/bin/macros.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
// ./src/macros.md
|
||||
|
||||
|
||||
// This is a simple macro named `say_hello`.
|
||||
macro_rules! say_hello {
|
||||
// `()` indicates that the macro takes no argument.
|
||||
() => {
|
||||
// The macro will expand into the contents of this block.
|
||||
println!("Hello!");
|
||||
};
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
// This call will expand into `println!("Hello");`
|
||||
say_hello!()
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
50
src/bin/macros/designators.rs
Normal file
50
src/bin/macros/designators.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
// ./src/macros/designators.md
|
||||
|
||||
|
||||
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 part0() {
|
||||
foo();
|
||||
bar();
|
||||
|
||||
print_result!(1u32 + 1);
|
||||
|
||||
// Recall that blocks are expressions too!
|
||||
print_result!({
|
||||
let x = 1u32;
|
||||
|
||||
x * x + 2 * x - 1
|
||||
});
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
26
src/bin/macros/dsl.rs
Normal file
26
src/bin/macros/dsl.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
// ./src/macros/dsl.md
|
||||
|
||||
|
||||
macro_rules! calculate {
|
||||
(eval $e:expr) => {
|
||||
{
|
||||
let val: usize = $e; // Force types to be integers
|
||||
println!("{} = {}", stringify!{$e}, val);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
calculate! {
|
||||
eval 1 + 2 // hehehe `eval` is _not_ a Rust keyword!
|
||||
}
|
||||
|
||||
calculate! {
|
||||
eval (1 + 2) * (3 / 4)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
32
src/bin/macros/overload.rs
Normal file
32
src/bin/macros/overload.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
// ./src/macros/overload.md
|
||||
|
||||
|
||||
// `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 part0() {
|
||||
test!(1i32 + 1 == 2i32; and 2i32 * 2 == 4i32);
|
||||
test!(true; or false);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
24
src/bin/macros/repeat.rs
Normal file
24
src/bin/macros/repeat.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
// ./src/macros/repeat.md
|
||||
|
||||
|
||||
// `find_min!` will calculate the minimum of any number of arguments.
|
||||
macro_rules! find_min {
|
||||
// Base case:
|
||||
($x:expr) => ($x);
|
||||
// `$x` followed by at least one `$y,`
|
||||
($x:expr, $($y:expr),+) => (
|
||||
// Call `find_min!` on the tail `$y`
|
||||
std::cmp::min($x, find_min!($($y),+))
|
||||
)
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
println!("{}", find_min!(1));
|
||||
println!("{}", find_min!(1 + 2, 2));
|
||||
println!("{}", find_min!(5, 2 * 3, 4));
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
31
src/bin/macros/variadics.rs
Normal file
31
src/bin/macros/variadics.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
// ./src/macros/variadics.md
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
// Decompose multiple `eval`s recursively
|
||||
(eval $e:expr, $(eval $es:expr),+) => {{
|
||||
calculate! { eval $e }
|
||||
calculate! { $(eval $es),+ }
|
||||
}};
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
calculate! { // Look ma! Variadic `calculate!`!
|
||||
eval 1 + 2,
|
||||
eval 3 + 4,
|
||||
eval (2 * 3) + 1
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
54
src/bin/meta/doc.rs
Normal file
54
src/bin/meta/doc.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
// ./src/meta/doc.md
|
||||
|
||||
|
||||
#![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
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// // 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(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gives a friendly hello!
|
||||
///
|
||||
/// Says "Hello, [name](Person::name)" to the `Person` it is called on.
|
||||
pub fn hello(& self) {
|
||||
println!("Hello, {}!", self.name);
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
let john = Person::new("John");
|
||||
|
||||
john.hello();
|
||||
}
|
||||
|
||||
// Example from the futures-rs library
|
||||
#[doc(hidden)]
|
||||
pub use self::async_await::*;
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
11
src/bin/meta/playground.rs
Normal file
11
src/bin/meta/playground.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
// ./src/meta/playground.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
println!("Hello World!");
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
51
src/bin/mod/struct_visibility.rs
Normal file
51
src/bin/mod/struct_visibility.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
// ./src/mod/struct_visibility.md
|
||||
|
||||
|
||||
mod my {
|
||||
// A public struct with a public field of generic type `T`
|
||||
pub struct OpenBox<T> {
|
||||
pub contents: T,
|
||||
}
|
||||
|
||||
// A public struct with a private field of generic type `T`
|
||||
#[allow(dead_code)]
|
||||
pub struct ClosedBox<T> {
|
||||
contents: T,
|
||||
}
|
||||
|
||||
impl<T> ClosedBox<T> {
|
||||
// A public constructor method
|
||||
pub fn new(contents: T) -> ClosedBox<T> {
|
||||
ClosedBox {
|
||||
contents: contents,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
// Public structs with public fields can be constructed as usual
|
||||
let open_box = my::OpenBox { contents: "public information" };
|
||||
|
||||
// and their fields can be normally accessed.
|
||||
println!("The open box contains: {}", open_box.contents);
|
||||
|
||||
// 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
|
||||
|
||||
// However, structs with private fields can be created using
|
||||
// public constructors
|
||||
let _closed_box = my::ClosedBox::new("classified information");
|
||||
|
||||
// and the private fields of a public struct cannot be accessed.
|
||||
// Error! The `contents` field is private
|
||||
//println!("The closed box contains: {}", _closed_box.contents);
|
||||
// TODO ^ Try uncommenting this line
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
57
src/bin/mod/super.rs
Normal file
57
src/bin/mod/super.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
// ./src/mod/super.md
|
||||
|
||||
|
||||
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();
|
||||
|
||||
// We can also use `self` to access another module inside `my`:
|
||||
self::cool::function();
|
||||
|
||||
// The `super` keyword refers to the parent scope (outside the `my` module).
|
||||
super::function();
|
||||
|
||||
// This will bind to the `cool::function` in the *crate* scope.
|
||||
// In this case the crate scope is the outermost scope.
|
||||
{
|
||||
use crate::cool::function as root_function;
|
||||
root_function();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
my::indirect_call();
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
53
src/bin/mod/use.rs
Normal file
53
src/bin/mod/use.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
// ./src/mod/use.md
|
||||
|
||||
|
||||
use crate::deeply::nested::{
|
||||
my_first_function,
|
||||
my_second_function,
|
||||
AndATraitType
|
||||
};
|
||||
|
||||
fn part0() {
|
||||
my_first_function();
|
||||
}
|
||||
|
||||
// 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 part1() {
|
||||
// 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 crate::deeply::nested::function;
|
||||
|
||||
// `use` bindings have a local scope. In this case, the
|
||||
// shadowing of `function()` is only in this block.
|
||||
function();
|
||||
|
||||
println!("Leaving block");
|
||||
}
|
||||
|
||||
function();
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
part1();
|
||||
}
|
||||
|
128
src/bin/mod/visibility.rs
Normal file
128
src/bin/mod/visibility.rs
Normal file
|
@ -0,0 +1,128 @@
|
|||
// ./src/mod/visibility.md
|
||||
|
||||
|
||||
// A module named `my_mod`
|
||||
mod my_mod {
|
||||
// Items in modules default to private visibility.
|
||||
fn private_function() {
|
||||
println!("called `my_mod::private_function()`");
|
||||
}
|
||||
|
||||
// Use the `pub` modifier to override default visibility.
|
||||
pub fn function() {
|
||||
println!("called `my_mod::function()`");
|
||||
}
|
||||
|
||||
// Items can access other items in the same module,
|
||||
// even when private.
|
||||
pub fn indirect_access() {
|
||||
print!("called `my_mod::indirect_access()`, that\n> ");
|
||||
private_function();
|
||||
}
|
||||
|
||||
// Modules can also be nested
|
||||
pub mod nested {
|
||||
pub fn function() {
|
||||
println!("called `my_mod::nested::function()`");
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn private_function() {
|
||||
println!("called `my_mod::nested::private_function()`");
|
||||
}
|
||||
|
||||
// Functions declared using `pub(in path)` syntax are only visible
|
||||
// within the given path. `path` must be a parent or ancestor module
|
||||
pub(in crate::my_mod) fn public_function_in_my_mod() {
|
||||
print!("called `my_mod::nested::public_function_in_my_mod()`, that\n> ");
|
||||
public_function_in_nested();
|
||||
}
|
||||
|
||||
// Functions declared using `pub(self)` syntax are only visible within
|
||||
// the current module, which is the same as leaving them private
|
||||
pub(self) fn public_function_in_nested() {
|
||||
println!("called `my_mod::nested::public_function_in_nested()`");
|
||||
}
|
||||
|
||||
// Functions declared using `pub(super)` syntax are only visible within
|
||||
// the parent module
|
||||
pub(super) fn public_function_in_super_mod() {
|
||||
println!("called `my_mod::nested::public_function_in_super_mod()`");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_public_function_in_my_mod() {
|
||||
print!("called `my_mod::call_public_function_in_my_mod()`, that\n> ");
|
||||
nested::public_function_in_my_mod();
|
||||
print!("> ");
|
||||
nested::public_function_in_super_mod();
|
||||
}
|
||||
|
||||
// pub(crate) makes functions visible only within the current crate
|
||||
pub(crate) fn public_function_in_crate() {
|
||||
println!("called `my_mod::public_function_in_crate()`");
|
||||
}
|
||||
|
||||
// Nested modules follow the same rules for visibility
|
||||
mod private_nested {
|
||||
#[allow(dead_code)]
|
||||
pub fn function() {
|
||||
println!("called `my_mod::private_nested::function()`");
|
||||
}
|
||||
|
||||
// Private parent items will still restrict the visibility of a child item,
|
||||
// even if it is declared as visible within a bigger scope.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn restricted_function() {
|
||||
println!("called `my_mod::private_nested::restricted_function()`");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn function() {
|
||||
println!("called `function()`");
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
// Modules allow disambiguation between items that have the same name.
|
||||
function();
|
||||
my_mod::function();
|
||||
|
||||
// Public items, including those inside nested modules, can be
|
||||
// accessed from outside the parent module.
|
||||
my_mod::indirect_access();
|
||||
my_mod::nested::function();
|
||||
my_mod::call_public_function_in_my_mod();
|
||||
|
||||
// pub(crate) items can be called from anywhere in the same crate
|
||||
my_mod::public_function_in_crate();
|
||||
|
||||
// pub(in path) items can only be called from within the module specified
|
||||
// Error! function `public_function_in_my_mod` is private
|
||||
//my_mod::nested::public_function_in_my_mod();
|
||||
// TODO ^ Try uncommenting this line
|
||||
|
||||
// Private items of a module cannot be directly accessed, even if
|
||||
// nested in a public module:
|
||||
|
||||
// Error! `private_function` is private
|
||||
//my_mod::private_function();
|
||||
// TODO ^ Try uncommenting this line
|
||||
|
||||
// Error! `private_function` is private
|
||||
//my_mod::nested::private_function();
|
||||
// TODO ^ Try uncommenting this line
|
||||
|
||||
// Error! `private_nested` is a private module
|
||||
//my_mod::private_nested::function();
|
||||
// TODO ^ Try uncommenting this line
|
||||
|
||||
// Error! `private_nested` is a private module
|
||||
//my_mod::private_nested::restricted_function();
|
||||
// TODO ^ Try uncommenting this line
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
33
src/bin/primitives.rs
Normal file
33
src/bin/primitives.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
// ./src/primitives.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
// Variables can be type annotated.
|
||||
let logical: bool = true;
|
||||
|
||||
let a_float: f64 = 1.0; // Regular annotation
|
||||
let an_integer = 5i32; // Suffix annotation
|
||||
|
||||
// Or a default will be used.
|
||||
let default_float = 3.0; // `f64`
|
||||
let default_integer = 7; // `i32`
|
||||
|
||||
// A type can also be inferred from context
|
||||
let mut inferred_type = 12; // Type i64 is inferred from another line
|
||||
inferred_type = 4294967296i64;
|
||||
|
||||
// A mutable variable's value can be changed.
|
||||
let mut mutable = 12; // Mutable `i32`
|
||||
mutable = 21;
|
||||
|
||||
// Error! The type of a variable can't be changed.
|
||||
// mutable = true;
|
||||
|
||||
// Variables can be overwritten with shadowing.
|
||||
let mutable = true;
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
63
src/bin/primitives/array.rs
Normal file
63
src/bin/primitives/array.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
// ./src/primitives/array.md
|
||||
|
||||
|
||||
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 part0() {
|
||||
// Fixed-size array (type signature is superfluous)
|
||||
let xs: [i32; 5] = [1, 2, 3, 4, 5];
|
||||
|
||||
// All elements can be initialized to the same value
|
||||
let ys: [i32; 500] = [0; 500];
|
||||
|
||||
// Indexing starts at 0
|
||||
println!("first element of the array: {}", xs[0]);
|
||||
println!("second element of the array: {}", xs[1]);
|
||||
|
||||
// `len` returns the count of elements in the array
|
||||
println!("number of elements in array: {}", xs.len());
|
||||
|
||||
// Arrays are stack allocated
|
||||
println!("array occupies {} bytes", mem::size_of_val(&xs));
|
||||
|
||||
// Arrays can be automatically borrowed as slices
|
||||
println!("borrow the whole array as a slice");
|
||||
analyze_slice(&xs);
|
||||
|
||||
// Slices can point to a section of an array
|
||||
// They are of the form [starting_index..ending_index]
|
||||
// starting_index is the first position in the slice
|
||||
// ending_index is one more than the last position in the slice
|
||||
println!("borrow a section of the array as a slice");
|
||||
analyze_slice(&ys[1 .. 4]);
|
||||
|
||||
// Example of empty slice `&[]`
|
||||
let empty_array: [u32; 0] = [];
|
||||
assert_eq!(&empty_array, &[]);
|
||||
assert_eq!(&empty_array, &[][..]); // same but more verbose
|
||||
|
||||
// Arrays can be safely accessed using `.get`, which returns an
|
||||
// `Option`. This can be matched as shown below, or used with
|
||||
// `.expect()` if you would like the program to exit with a nice
|
||||
// message instead of happily continue.
|
||||
for i in 0..xs.len() + 1 { // OOPS, one element too far
|
||||
match xs.get(i) {
|
||||
Some(xval) => println!("{}: {}", i, xval),
|
||||
None => println!("Slow down! {} is too far!", i),
|
||||
}
|
||||
}
|
||||
|
||||
// Out of bound indexing causes compile error
|
||||
//println!("{}", xs[5]);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
31
src/bin/primitives/literals.rs
Normal file
31
src/bin/primitives/literals.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
// ./src/primitives/literals.md
|
||||
|
||||
|
||||
fn part0() {
|
||||
// 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
|
||||
|
||||
// Short-circuiting boolean logic
|
||||
println!("true AND false is {}", true && false);
|
||||
println!("true OR false is {}", true || false);
|
||||
println!("NOT true is {}", !true);
|
||||
|
||||
// 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);
|
||||
|
||||
// Use underscores to improve readability!
|
||||
println!("One million is written as {}", 1_000_000u32);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
62
src/bin/primitives/tuples.rs
Normal file
62
src/bin/primitives/tuples.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
// ./src/primitives/tuples.md
|
||||
|
||||
|
||||
// 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 (int_param, bool_param) = pair;
|
||||
|
||||
(bool_param, int_param)
|
||||
}
|
||||
|
||||
// The following struct is for the activity.
|
||||
#[derive(Debug)]
|
||||
struct Matrix(f32, f32, f32, f32);
|
||||
|
||||
fn part0() {
|
||||
// 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);
|
||||
|
||||
// Values can be extracted from the tuple using tuple indexing
|
||||
println!("long tuple first value: {}", long_tuple.0);
|
||||
println!("long tuple second value: {}", long_tuple.1);
|
||||
|
||||
// Tuples can be tuple members
|
||||
let tuple_of_tuples = ((1u8, 2u16, 2u32), (4u64, -1i8), -2i16);
|
||||
|
||||
// Tuples are printable
|
||||
println!("tuple of tuples: {:?}", tuple_of_tuples);
|
||||
|
||||
// But long Tuples (more than 12 elements) cannot be printed
|
||||
// let too_long_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
|
||||
// println!("too long tuple: {:?}", too_long_tuple);
|
||||
// TODO ^ Uncomment the above 2 lines to see the compiler error
|
||||
|
||||
let pair = (1, true);
|
||||
println!("pair is {:?}", pair);
|
||||
|
||||
println!("the reversed pair is {:?}", reverse(pair));
|
||||
|
||||
// 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));
|
||||
|
||||
//tuples can be destructured to create bindings
|
||||
let tuple = (1, "hello", 4.5, true);
|
||||
|
||||
let (a, b, c, d) = tuple;
|
||||
println!("{:?}, {:?}, {:?}, {:?}", a, b, c, d);
|
||||
|
||||
let matrix = Matrix(1.1, 1.2, 2.1, 2.2);
|
||||
println!("{:?}", matrix);
|
||||
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate libc;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
// Since we are passing a C string the final null character is mandatory
|
||||
const HELLO: &'static str = "Hello, world! %d + %d = %d\n\0";
|
||||
let x: i32 = 1;
|
||||
let y: i32 = 2;
|
||||
unsafe {
|
||||
libc::printf(HELLO.as_ptr() as *const _, x, y, x+y);
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn my_panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
loop {}
|
||||
}
|
45
src/bin/scope/borrow.rs
Normal file
45
src/bin/scope/borrow.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
// ./src/scope/borrow.md
|
||||
|
||||
|
||||
// This function takes ownership of a box and destroys it
|
||||
fn eat_box_i32(boxed_i32: Box<i32>) {
|
||||
println!("Destroying box that contains {}", boxed_i32);
|
||||
}
|
||||
|
||||
// This function borrows an i32
|
||||
fn borrow_i32(borrowed_i32: &i32) {
|
||||
println!("This int is: {}", borrowed_i32);
|
||||
}
|
||||
|
||||
fn part0() {
|
||||
// Create a boxed i32, and a stacked i32
|
||||
let boxed_i32 = Box::new(5_i32);
|
||||
let stacked_i32 = 6_i32;
|
||||
|
||||
// Borrow the contents of the box. Ownership is not taken,
|
||||
// so the contents can be borrowed again.
|
||||
borrow_i32(&boxed_i32);
|
||||
borrow_i32(&stacked_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 later in scope.
|
||||
// eat_box_i32(boxed_i32);
|
||||
// FIXME ^ Comment out this line
|
||||
|
||||
// Attempt to borrow `_ref_to_i32` after inner value is destroyed
|
||||
borrow_i32(_ref_to_i32);
|
||||
// `_ref_to_i32` goes out of scope and is no longer borrowed.
|
||||
}
|
||||
|
||||
// `boxed_i32` can now give up ownership to `eat_box` and be destroyed
|
||||
eat_box_i32(boxed_i32);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
57
src/bin/scope/borrow/alias.rs
Normal file
57
src/bin/scope/borrow/alias.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
// ./src/scope/borrow/alias.md
|
||||
|
||||
|
||||
struct Point { x: i32, y: i32, z: i32 }
|
||||
|
||||
fn part0() {
|
||||
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);
|
||||
|
||||
// Error! Can't borrow `point` as mutable because it's currently
|
||||
// borrowed as immutable.
|
||||
// let mutable_borrow = &mut point;
|
||||
// TODO ^ Try uncommenting this line
|
||||
|
||||
// The borrowed values are used again here
|
||||
println!("Point has coordinates: ({}, {}, {})",
|
||||
borrowed_point.x, another_borrow.y, point.z);
|
||||
|
||||
// The immutable references are no longer used for the rest of the code so
|
||||
// it is possible to reborrow with a mutable reference.
|
||||
let mutable_borrow = &mut point;
|
||||
|
||||
// Change data via mutable reference
|
||||
mutable_borrow.x = 5;
|
||||
mutable_borrow.y = 2;
|
||||
mutable_borrow.z = 1;
|
||||
|
||||
// Error! Can't borrow `point` as immutable because it's currently
|
||||
// borrowed as mutable.
|
||||
// let y = &point.y;
|
||||
// TODO ^ Try uncommenting this line
|
||||
|
||||
// Error! Can't print because `println!` takes an immutable reference.
|
||||
// println!("Point Z coordinate is {}", point.z);
|
||||
// TODO ^ Try uncommenting this line
|
||||
|
||||
// Ok! Mutable references can be passed as immutable to `println!`
|
||||
println!("Point has coordinates: ({}, {}, {})",
|
||||
mutable_borrow.x, mutable_borrow.y, mutable_borrow.z);
|
||||
|
||||
// The mutable reference is no longer used for the rest of the code so it
|
||||
// is possible to reborrow
|
||||
let new_borrowed_point = &point;
|
||||
println!("Point now has coordinates: ({}, {}, {})",
|
||||
new_borrowed_point.x, new_borrowed_point.y, new_borrowed_point.z);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
part0();
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue