diff --git a/src/FlowOfControl.rs b/src/FlowOfControl.rs new file mode 100644 index 0000000..95f3d93 --- /dev/null +++ b/src/FlowOfControl.rs @@ -0,0 +1,7 @@ +pub mod IfElse; +pub mod Loop; +pub mod While; +pub mod ForAndRange; +pub mod Match; +pub mod IfLet; +pub mod WhileLet; diff --git a/src/FlowOfControl/ForAndRange.rs b/src/FlowOfControl/ForAndRange.rs new file mode 100644 index 0000000..b5e661a --- /dev/null +++ b/src/FlowOfControl/ForAndRange.rs @@ -0,0 +1,60 @@ +pub fn main(){ + //`n` will take the values: 1, 2, ..., 100 in each iteration + for n in 1..101{ + if((n % 15) == 0){ + println!("fizzbuzz"); + } + else if((n % 3) == 0){ + println!("fizz"); + } + else if((n % 5) == 0){ + println!("buzz"); + } + else{ + println!("{}", n); + } + } + + // `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); + } + } + + //This borrows each element of the collection through each iteration. Thus leaving the collection untouched and available for reuse after the loop. + let names = vec!["Bob", "Frank", "Ferris"]; + for name in names.iter(){ + match name { + &"Ferris" => println!("There is a rustacean among us!"), + _ => println!("Hello {}", name), + } + } + //This consumes the collection so that on each iteration the exact data is provided. + //Once the collection has been consumed it is no longer available for reuse as it has been 'moved' within the loop. + for name in names.into_iter(){ + match name{ + "Ferris" => println!("There is a rustacean among us!"), + _ => println!("Hello {}", name), + } + } + //This mutably borrows each element of the collection, allowing for the collection to be modified in place. + let mut mutNames = vec!["Bob", "Frank", "Ferris"]; + for name in mutNames.iter_mut(){ + *name = match name{ + &mut "Ferris" => "There is a rustacean among us!", + _ => "Hello", + } + } + + println!("mutNames: {:?}", mutNames); +} diff --git a/src/FlowOfControl/IfElse.rs b/src/FlowOfControl/IfElse.rs new file mode 100644 index 0000000..115dd4a --- /dev/null +++ b/src/FlowOfControl/IfElse.rs @@ -0,0 +1,31 @@ +pub fn main(){ + let n = 5; + + if(n < 0){ + print!("{} is negative", n); + } + else if(n > 0){ + print!("{} is positive", n); + } + else{ + print!("{} is zero", n); + } + + let big_n = + if((n < 10) && (n > -10)){ + println!(", and is a small number, increase ten-fold"); + + // 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); +} diff --git a/src/FlowOfControl/IfLet.rs b/src/FlowOfControl/IfLet.rs new file mode 100644 index 0000000..87dfc1f --- /dev/null +++ b/src/FlowOfControl/IfLet.rs @@ -0,0 +1,86 @@ +// Our example enum +enum Foo{ + Bar, + Baz, + Qux(u32) +} + +//This enum purposely neither implements nor derives PartialEq. +//That is why comparing Foo::Bar == a fails below. +/* +enum Foo {Bar} +fn main(){ + 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(){ + //All have type `Option` + let number = Some(7); + let letter: Option = None; + let emoticon: Option = 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 :)!"); + } + + //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` + #[allow(unused_variables)] + if let Foo::Qux(value @ 100) = c{ + println!("c is one hundred"); + } +} diff --git a/src/FlowOfControl/Loop.rs b/src/FlowOfControl/Loop.rs new file mode 100644 index 0000000..f03782a --- /dev/null +++ b/src/FlowOfControl/Loop.rs @@ -0,0 +1,29 @@ +pub mod NestingAndLabels; +pub mod ReturningFromLoops; + +pub fn main(){ + 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; + } + } +} diff --git a/src/FlowOfControl/Loop/NestingAndLabels.rs b/src/FlowOfControl/Loop/NestingAndLabels.rs new file mode 100644 index 0000000..1733884 --- /dev/null +++ b/src/FlowOfControl/Loop/NestingAndLabels.rs @@ -0,0 +1,22 @@ +#[allow(unreachable_code)] +#[allow(unused_labels)] + + +pub fn main(){ + '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"); +} diff --git a/src/FlowOfControl/Loop/ReturningFromLoops.rs b/src/FlowOfControl/Loop/ReturningFromLoops.rs new file mode 100644 index 0000000..c256fbf --- /dev/null +++ b/src/FlowOfControl/Loop/ReturningFromLoops.rs @@ -0,0 +1,13 @@ +pub fn main(){ + let mut counter = 0; + + let result = loop{ + counter += 1; + + if(counter == 10){ + break counter * 2; + } + }; + + assert_eq!(result, 20); +} diff --git a/src/FlowOfControl/Match.rs b/src/FlowOfControl/Match.rs new file mode 100644 index 0000000..d1aa7bd --- /dev/null +++ b/src/FlowOfControl/Match.rs @@ -0,0 +1,31 @@ +pub mod Destructuring; +pub mod Guards; +pub mod Binding; + +pub fn main() { + 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"), + //Match an inclusive range + 13..=19 => println!("A teen"), + //Handle the rest of cases + _ => println!("Ain't special"), + } + + 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); +} diff --git a/src/FlowOfControl/Match/Binding.rs b/src/FlowOfControl/Match/Binding.rs new file mode 100644 index 0000000..5f4bf87 --- /dev/null +++ b/src/FlowOfControl/Match/Binding.rs @@ -0,0 +1,33 @@ +// A function `age` which returns a `u32`. +fn age() -> u32{ + 15 +} + +fn some_number() -> Option{ + Some(42) +} + +pub fn main(){ + println!("Tell me what type of person you are"); + + match age(){ + 0 => println!("I'm not born yet I guess"), + //Could `match` 1 ..= 12 directly but then what age + //would the child be? Instead, bind to `n` for the + //sequence of 1 ..= 12. Now the age can be reported. + n @ 1 ..= 12 => println!("I'm a child of age {:?}", n), + n @ 13 ..= 19 => println!("I'm a teen of age {:?}", n), + //Nothing bound. Return the result. + n => println!("I'm an old person of age {:?}", n), + } + + 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). + _ => (), + } +} diff --git a/src/FlowOfControl/Match/Destructuring.rs b/src/FlowOfControl/Match/Destructuring.rs new file mode 100644 index 0000000..7cda181 --- /dev/null +++ b/src/FlowOfControl/Match/Destructuring.rs @@ -0,0 +1,4 @@ +pub mod Tuples; +pub mod Enums; +pub mod PointersRef; +pub mod Structs; diff --git a/src/FlowOfControl/Match/Destructuring/Enums.rs b/src/FlowOfControl/Match/Destructuring/Enums.rs new file mode 100644 index 0000000..0561611 --- /dev/null +++ b/src/FlowOfControl/Match/Destructuring/Enums.rs @@ -0,0 +1,39 @@ +//`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), +} + +pub fn main(){ + 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 + } +} diff --git a/src/FlowOfControl/Match/Destructuring/PointersRef.rs b/src/FlowOfControl/Match/Destructuring/PointersRef.rs new file mode 100644 index 0000000..c8c51a3 --- /dev/null +++ b/src/FlowOfControl/Match/Destructuring/PointersRef.rs @@ -0,0 +1,43 @@ +pub fn main(){ + //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); + }, + } +} diff --git a/src/FlowOfControl/Match/Destructuring/Structs.rs b/src/FlowOfControl/Match/Destructuring/Structs.rs new file mode 100644 index 0000000..ece9133 --- /dev/null +++ b/src/FlowOfControl/Match/Destructuring/Structs.rs @@ -0,0 +1,22 @@ +pub fn main(){ + 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); + } +} diff --git a/src/FlowOfControl/Match/Destructuring/Tuples.rs b/src/FlowOfControl/Match/Destructuring/Tuples.rs new file mode 100644 index 0000000..3137b12 --- /dev/null +++ b/src/FlowOfControl/Match/Destructuring/Tuples.rs @@ -0,0 +1,14 @@ +pub fn main(){ + let pair = (0, -2); + //TODO ^ Try different values for `pair` + + println!("Tell me about {:?}", pair); + //Match can be used to destructure a tuple + match pair{ + //Destructure the second + (0, y) => println!("First is `0` and `y` is `{:?}`", y), + (x, 0) => println!("`x` is `{:?}` and last is `0`", x), + _ => println!("It doesn't matter what they are"), + //`_` means don't bind the value to a variable + } +} diff --git a/src/FlowOfControl/Match/Guards.rs b/src/FlowOfControl/Match/Guards.rs new file mode 100644 index 0000000..0af72a7 --- /dev/null +++ b/src/FlowOfControl/Match/Guards.rs @@ -0,0 +1,13 @@ +pub fn main(){ + let pair = (2, -2); + //TODO ^ Try different values for `pair` + + println!("Tell me about {:?}", pair); + match pair{ + (x, y) if x == y => println!("These are twins"), + //The ^ `if condition` part is a guard + (x, y) if x + y == 0 => println!("Antimatter, kaboom!"), + (x, _) if x % 2 == 1 => println!("The first one is odd"), + _ => println!("No correlation..."), + } +} diff --git a/src/FlowOfControl/While.rs b/src/FlowOfControl/While.rs new file mode 100644 index 0000000..533a4da --- /dev/null +++ b/src/FlowOfControl/While.rs @@ -0,0 +1,23 @@ +pub fn main(){ + //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; + } +} diff --git a/src/FlowOfControl/WhileLet.rs b/src/FlowOfControl/WhileLet.rs new file mode 100644 index 0000000..2802176 --- /dev/null +++ b/src/FlowOfControl/WhileLet.rs @@ -0,0 +1,19 @@ +pub fn main(){ + // Make `optional` of type `Option` + 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. +} diff --git a/src/main.rs b/src/main.rs index f72c077..e7ebd07 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ #![allow(non_snake_case)] #![allow(dead_code)] +#![allow(unused_parens)] mod HelloWorld; mod Primitives; @@ -8,6 +9,7 @@ mod VariableBindings; mod Types; mod Conversion; mod Expressions; +mod FlowOfControl; fn main(){ @@ -77,5 +79,38 @@ fn main(){ //Conversion::ToAndFromStrings::main(); //7 Expressions - Expressions::main(); + //Expressions::main(); + + //8 Flow of Control + //8.1 if/else + FlowOfControl::IfElse::main(); + //8.2 loop + FlowOfControl::Loop::main(); + //8.2.1 Nesting and labels + FlowOfControl::Loop::NestingAndLabels::main(); + //8.2.2 Returning from loops + FlowOfControl::Loop::ReturningFromLoops::main(); + //8.3 while + FlowOfControl::While::main(); + //8.4 for and range + FlowOfControl::ForAndRange::main(); + //8.5 match + FlowOfControl::Match::main(); + //8.5.1 Destructuring + //8.5.1.1 tuples + FlowOfControl::Match::Destructuring::Tuples::main(); + //8.5.1.2 enums + FlowOfControl::Match::Destructuring::Enums::main(); + //8.5.1.3 pointers/ref + FlowOfControl::Match::Destructuring::PointersRef::main(); + //8.5.1.4 structs + FlowOfControl::Match::Destructuring::Structs::main(); + //8.5.2 Guards + FlowOfControl::Match::Guards::main(); + //8.5.3 Binding + FlowOfControl::Match::Binding::main(); + //8.6 if let + FlowOfControl::IfLet::main(); + //8.7 while let + FlowOfControl::WhileLet::main(); }