From d976dc883b1c3d97f440e5b9ca547010cd2db210 Mon Sep 17 00:00:00 2001 From: ahgamut <41098605+ahgamut@users.noreply.github.com> Date: Wed, 7 Sep 2022 10:41:09 +0530 Subject: [PATCH] added all examples from rust by example via panflute and https://github.com/rust-lang/rust-by-example --- src/bin/attribute/cfg.rs | 30 ++++ src/bin/attribute/cfg/custom.rs | 16 ++ src/bin/attribute/unused.rs | 20 +++ src/bin/conversion/from_into.rs | 46 +++++ src/bin/conversion/string.rs | 33 ++++ src/bin/conversion/try_from_try_into.rs | 39 +++++ src/bin/custom_types/constants.rs | 29 ++++ src/bin/custom_types/enum.rs | 83 +++++++++ src/bin/custom_types/enum/c_like.rs | 33 ++++ src/bin/custom_types/enum/enum_use.rs | 45 +++++ .../custom_types/enum/testcase_linked_list.rs | 77 +++++++++ src/bin/custom_types/structs.rs | 83 +++++++++ src/bin/error/abort_unwind.rs | 40 +++++ src/bin/error/iter_result.rs | 73 ++++++++ src/bin/error/multiple_error_types.rs | 26 +++ .../multiple_error_types/boxing_errors.rs | 51 ++++++ .../multiple_error_types/define_error_type.rs | 57 +++++++ .../multiple_error_types/option_result.rs | 50 ++++++ .../reenter_question_mark.rs | 49 ++++++ .../error/multiple_error_types/wrap_error.rs | 87 ++++++++++ src/bin/error/option_unwrap.rs | 44 +++++ src/bin/error/option_unwrap/and_then.rs | 60 +++++++ src/bin/error/option_unwrap/defaults.rs | 94 ++++++++++ src/bin/error/option_unwrap/map.rs | 69 ++++++++ src/bin/error/option_unwrap/question_mark.rs | 53 ++++++ src/bin/error/panic.rs | 19 +++ src/bin/error/result.rs | 35 ++++ src/bin/error/result/early_returns.rs | 35 ++++ src/bin/error/result/enter_question_mark.rs | 53 ++++++ src/bin/error/result/result_alias.rs | 32 ++++ src/bin/error/result/result_map.rs | 70 ++++++++ src/bin/example1.rs | 146 ---------------- src/bin/example2.rs | 160 ------------------ src/bin/example3.rs | 85 ---------- src/bin/expression.rs | 46 +++++ src/bin/flow_control/for.rs | 82 +++++++++ src/bin/flow_control/if_else.rs | 36 ++++ src/bin/flow_control/if_let.rs | 94 ++++++++++ src/bin/flow_control/loop.rs | 34 ++++ src/bin/flow_control/loop/nested.rs | 29 ++++ src/bin/flow_control/loop/return.rs | 21 +++ src/bin/flow_control/match.rs | 37 ++++ src/bin/flow_control/match/binding.rs | 44 +++++ .../match/destructuring/destructure_enum.rs | 48 ++++++ .../destructuring/destructure_pointers.rs | 58 +++++++ .../match/destructuring/destructure_slice.rs | 46 +++++ .../destructuring/destructure_structures.rs | 30 ++++ .../match/destructuring/destructure_tuple.rs | 25 +++ src/bin/flow_control/match/guard.rs | 38 +++++ src/bin/flow_control/while.rs | 28 +++ src/bin/flow_control/while_let.rs | 28 +++ src/bin/fn.rs | 45 +++++ src/bin/fn/closures.rs | 34 ++++ src/bin/fn/closures/anonymity.rs | 25 +++ src/bin/fn/closures/capture.rs | 96 +++++++++++ .../fn/closures/closure_examples/iter_any.rs | 33 ++++ .../fn/closures/closure_examples/iter_find.rs | 47 +++++ src/bin/fn/closures/input_functions.rs | 26 +++ src/bin/fn/closures/input_parameters.rs | 59 +++++++ src/bin/fn/closures/output_parameters.rs | 35 ++++ src/bin/fn/hof.rs | 42 +++++ src/bin/fn/methods.rs | 117 +++++++++++++ src/bin/generics.rs | 35 ++++ src/bin/generics/assoc_items/the_problem.rs | 52 ++++++ src/bin/generics/assoc_items/types.rs | 58 +++++++ src/bin/generics/bounds.rs | 46 +++++ src/bin/generics/bounds/testcase_empty.rs | 35 ++++ src/bin/generics/gen_fn.rs | 45 +++++ src/bin/generics/gen_trait.rs | 38 +++++ src/bin/generics/impl.rs | 36 ++++ src/bin/generics/multi_bounds.rs | 31 ++++ src/bin/generics/phantom.rs | 47 +++++ src/bin/generics/phantom/testcase_units.rs | 56 ++++++ src/bin/generics/where.rs | 30 ++++ src/bin/{hello_world.rs => hello.rs} | 11 +- src/bin/hello/comment.rs | 38 +++++ src/bin/hello/print.rs | 68 ++++++++ src/bin/hello/print/fmt.rs | 56 ++++++ src/bin/hello/print/print_debug.rs | 49 ++++++ src/bin/hello/print/print_display.rs | 62 +++++++ .../print/print_display/testcase_list.rs | 39 +++++ src/bin/macros.rs | 21 +++ src/bin/macros/designators.rs | 50 ++++++ src/bin/macros/dsl.rs | 26 +++ src/bin/macros/overload.rs | 32 ++++ src/bin/macros/repeat.rs | 24 +++ src/bin/macros/variadics.rs | 31 ++++ src/bin/meta/doc.rs | 54 ++++++ src/bin/meta/playground.rs | 11 ++ src/bin/mod/struct_visibility.rs | 51 ++++++ src/bin/mod/super.rs | 57 +++++++ src/bin/mod/use.rs | 53 ++++++ src/bin/mod/visibility.rs | 128 ++++++++++++++ src/bin/primitives.rs | 33 ++++ src/bin/primitives/array.rs | 63 +++++++ src/bin/primitives/literals.rs | 31 ++++ src/bin/primitives/tuples.rs | 62 +++++++ src/bin/printf-nostd.rs | 22 --- src/bin/scope/borrow.rs | 45 +++++ src/bin/scope/borrow/alias.rs | 57 +++++++ src/bin/scope/borrow/mut.rs | 53 ++++++ src/bin/scope/borrow/ref.rs | 57 +++++++ src/bin/scope/lifetime.rs | 30 ++++ src/bin/scope/lifetime/elision.rs | 33 ++++ src/bin/scope/lifetime/explicit.rs | 41 +++++ src/bin/scope/lifetime/fn.rs | 50 ++++++ src/bin/scope/lifetime/lifetime_bounds.rs | 38 +++++ src/bin/scope/lifetime/lifetime_coercion.rs | 30 ++++ src/bin/scope/lifetime/methods.rs | 24 +++ src/bin/scope/lifetime/static_lifetime.rs | 23 +++ src/bin/scope/lifetime/struct.rs | 41 +++++ src/bin/scope/lifetime/trait.rs | 27 +++ src/bin/scope/move.rs | 50 ++++++ src/bin/scope/move/mut.rs | 26 +++ src/bin/scope/move/partial_move.rs | 33 ++++ src/bin/scope/raii.rs | 50 ++++++ src/bin/std/arc.rs | 31 ++++ src/bin/std/box.rs | 73 ++++++++ src/bin/std/hash.rs | 51 ++++++ src/bin/std/hash/alt_key_types.rs | 64 +++++++ src/bin/std/hash/hashset.rs | 43 +++++ src/bin/std/option.rs | 46 +++++ src/bin/std/panic.rs | 30 ++++ src/bin/std/rc.rs | 45 +++++ src/bin/std/result.rs | 66 ++++++++ src/bin/std/result/question_mark.rs | 71 ++++++++ src/bin/std/str.rs | 69 ++++++++ src/bin/std/vec.rs | 58 +++++++ src/bin/std_misc/arg.rs | 21 +++ src/bin/std_misc/arg/matching.rs | 73 ++++++++ src/bin/std_misc/channels.rs | 55 ++++++ src/bin/std_misc/file/open.rs | 32 ++++ src/bin/std_misc/path.rs | 35 ++++ src/bin/std_misc/process.rs | 27 +++ src/bin/std_misc/threads.rs | 29 ++++ .../std_misc/threads/testcase_mapreduce.rs | 101 +++++++++++ src/bin/trait.rs | 76 +++++++++ src/bin/trait/clone.rs | 50 ++++++ src/bin/trait/derive.rs | 53 ++++++ src/bin/trait/disambiguating.rs | 52 ++++++ src/bin/trait/drop.rs | 48 ++++++ src/bin/trait/dyn.rs | 45 +++++ src/bin/trait/impl_trait.rs | 94 ++++++++++ src/bin/trait/iter.rs | 79 +++++++++ src/bin/trait/ops.rs | 49 ++++++ src/bin/trait/supertraits.rs | 39 +++++ src/bin/types/alias.rs | 25 +++ src/bin/types/cast.rs | 86 ++++++++++ src/bin/types/inference.rs | 24 +++ src/bin/types/literals.rs | 25 +++ src/bin/unsafe.rs | 31 ++++ src/bin/variable_bindings.rs | 28 +++ src/bin/variable_bindings/declare.rs | 31 ++++ src/bin/variable_bindings/freeze.rs | 25 +++ src/bin/variable_bindings/mut.rs | 23 +++ src/bin/variable_bindings/scope.rs | 46 +++++ 156 files changed, 7028 insertions(+), 415 deletions(-) create mode 100644 src/bin/attribute/cfg.rs create mode 100644 src/bin/attribute/cfg/custom.rs create mode 100644 src/bin/attribute/unused.rs create mode 100644 src/bin/conversion/from_into.rs create mode 100644 src/bin/conversion/string.rs create mode 100644 src/bin/conversion/try_from_try_into.rs create mode 100644 src/bin/custom_types/constants.rs create mode 100644 src/bin/custom_types/enum.rs create mode 100644 src/bin/custom_types/enum/c_like.rs create mode 100644 src/bin/custom_types/enum/enum_use.rs create mode 100644 src/bin/custom_types/enum/testcase_linked_list.rs create mode 100644 src/bin/custom_types/structs.rs create mode 100644 src/bin/error/abort_unwind.rs create mode 100644 src/bin/error/iter_result.rs create mode 100644 src/bin/error/multiple_error_types.rs create mode 100644 src/bin/error/multiple_error_types/boxing_errors.rs create mode 100644 src/bin/error/multiple_error_types/define_error_type.rs create mode 100644 src/bin/error/multiple_error_types/option_result.rs create mode 100644 src/bin/error/multiple_error_types/reenter_question_mark.rs create mode 100644 src/bin/error/multiple_error_types/wrap_error.rs create mode 100644 src/bin/error/option_unwrap.rs create mode 100644 src/bin/error/option_unwrap/and_then.rs create mode 100644 src/bin/error/option_unwrap/defaults.rs create mode 100644 src/bin/error/option_unwrap/map.rs create mode 100644 src/bin/error/option_unwrap/question_mark.rs create mode 100644 src/bin/error/panic.rs create mode 100644 src/bin/error/result.rs create mode 100644 src/bin/error/result/early_returns.rs create mode 100644 src/bin/error/result/enter_question_mark.rs create mode 100644 src/bin/error/result/result_alias.rs create mode 100644 src/bin/error/result/result_map.rs delete mode 100644 src/bin/example1.rs delete mode 100644 src/bin/example2.rs delete mode 100644 src/bin/example3.rs create mode 100644 src/bin/expression.rs create mode 100644 src/bin/flow_control/for.rs create mode 100644 src/bin/flow_control/if_else.rs create mode 100644 src/bin/flow_control/if_let.rs create mode 100644 src/bin/flow_control/loop.rs create mode 100644 src/bin/flow_control/loop/nested.rs create mode 100644 src/bin/flow_control/loop/return.rs create mode 100644 src/bin/flow_control/match.rs create mode 100644 src/bin/flow_control/match/binding.rs create mode 100644 src/bin/flow_control/match/destructuring/destructure_enum.rs create mode 100644 src/bin/flow_control/match/destructuring/destructure_pointers.rs create mode 100644 src/bin/flow_control/match/destructuring/destructure_slice.rs create mode 100644 src/bin/flow_control/match/destructuring/destructure_structures.rs create mode 100644 src/bin/flow_control/match/destructuring/destructure_tuple.rs create mode 100644 src/bin/flow_control/match/guard.rs create mode 100644 src/bin/flow_control/while.rs create mode 100644 src/bin/flow_control/while_let.rs create mode 100644 src/bin/fn.rs create mode 100644 src/bin/fn/closures.rs create mode 100644 src/bin/fn/closures/anonymity.rs create mode 100644 src/bin/fn/closures/capture.rs create mode 100644 src/bin/fn/closures/closure_examples/iter_any.rs create mode 100644 src/bin/fn/closures/closure_examples/iter_find.rs create mode 100644 src/bin/fn/closures/input_functions.rs create mode 100644 src/bin/fn/closures/input_parameters.rs create mode 100644 src/bin/fn/closures/output_parameters.rs create mode 100644 src/bin/fn/hof.rs create mode 100644 src/bin/fn/methods.rs create mode 100644 src/bin/generics.rs create mode 100644 src/bin/generics/assoc_items/the_problem.rs create mode 100644 src/bin/generics/assoc_items/types.rs create mode 100644 src/bin/generics/bounds.rs create mode 100644 src/bin/generics/bounds/testcase_empty.rs create mode 100644 src/bin/generics/gen_fn.rs create mode 100644 src/bin/generics/gen_trait.rs create mode 100644 src/bin/generics/impl.rs create mode 100644 src/bin/generics/multi_bounds.rs create mode 100644 src/bin/generics/phantom.rs create mode 100644 src/bin/generics/phantom/testcase_units.rs create mode 100644 src/bin/generics/where.rs rename src/bin/{hello_world.rs => hello.rs} (83%) create mode 100644 src/bin/hello/comment.rs create mode 100644 src/bin/hello/print.rs create mode 100644 src/bin/hello/print/fmt.rs create mode 100644 src/bin/hello/print/print_debug.rs create mode 100644 src/bin/hello/print/print_display.rs create mode 100644 src/bin/hello/print/print_display/testcase_list.rs create mode 100644 src/bin/macros.rs create mode 100644 src/bin/macros/designators.rs create mode 100644 src/bin/macros/dsl.rs create mode 100644 src/bin/macros/overload.rs create mode 100644 src/bin/macros/repeat.rs create mode 100644 src/bin/macros/variadics.rs create mode 100644 src/bin/meta/doc.rs create mode 100644 src/bin/meta/playground.rs create mode 100644 src/bin/mod/struct_visibility.rs create mode 100644 src/bin/mod/super.rs create mode 100644 src/bin/mod/use.rs create mode 100644 src/bin/mod/visibility.rs create mode 100644 src/bin/primitives.rs create mode 100644 src/bin/primitives/array.rs create mode 100644 src/bin/primitives/literals.rs create mode 100644 src/bin/primitives/tuples.rs delete mode 100644 src/bin/printf-nostd.rs create mode 100644 src/bin/scope/borrow.rs create mode 100644 src/bin/scope/borrow/alias.rs create mode 100644 src/bin/scope/borrow/mut.rs create mode 100644 src/bin/scope/borrow/ref.rs create mode 100644 src/bin/scope/lifetime.rs create mode 100644 src/bin/scope/lifetime/elision.rs create mode 100644 src/bin/scope/lifetime/explicit.rs create mode 100644 src/bin/scope/lifetime/fn.rs create mode 100644 src/bin/scope/lifetime/lifetime_bounds.rs create mode 100644 src/bin/scope/lifetime/lifetime_coercion.rs create mode 100644 src/bin/scope/lifetime/methods.rs create mode 100644 src/bin/scope/lifetime/static_lifetime.rs create mode 100644 src/bin/scope/lifetime/struct.rs create mode 100644 src/bin/scope/lifetime/trait.rs create mode 100644 src/bin/scope/move.rs create mode 100644 src/bin/scope/move/mut.rs create mode 100644 src/bin/scope/move/partial_move.rs create mode 100644 src/bin/scope/raii.rs create mode 100644 src/bin/std/arc.rs create mode 100644 src/bin/std/box.rs create mode 100644 src/bin/std/hash.rs create mode 100644 src/bin/std/hash/alt_key_types.rs create mode 100644 src/bin/std/hash/hashset.rs create mode 100644 src/bin/std/option.rs create mode 100644 src/bin/std/panic.rs create mode 100644 src/bin/std/rc.rs create mode 100644 src/bin/std/result.rs create mode 100644 src/bin/std/result/question_mark.rs create mode 100644 src/bin/std/str.rs create mode 100644 src/bin/std/vec.rs create mode 100644 src/bin/std_misc/arg.rs create mode 100644 src/bin/std_misc/arg/matching.rs create mode 100644 src/bin/std_misc/channels.rs create mode 100644 src/bin/std_misc/file/open.rs create mode 100644 src/bin/std_misc/path.rs create mode 100644 src/bin/std_misc/process.rs create mode 100644 src/bin/std_misc/threads.rs create mode 100644 src/bin/std_misc/threads/testcase_mapreduce.rs create mode 100644 src/bin/trait.rs create mode 100644 src/bin/trait/clone.rs create mode 100644 src/bin/trait/derive.rs create mode 100644 src/bin/trait/disambiguating.rs create mode 100644 src/bin/trait/drop.rs create mode 100644 src/bin/trait/dyn.rs create mode 100644 src/bin/trait/impl_trait.rs create mode 100644 src/bin/trait/iter.rs create mode 100644 src/bin/trait/ops.rs create mode 100644 src/bin/trait/supertraits.rs create mode 100644 src/bin/types/alias.rs create mode 100644 src/bin/types/cast.rs create mode 100644 src/bin/types/inference.rs create mode 100644 src/bin/types/literals.rs create mode 100644 src/bin/unsafe.rs create mode 100644 src/bin/variable_bindings.rs create mode 100644 src/bin/variable_bindings/declare.rs create mode 100644 src/bin/variable_bindings/freeze.rs create mode 100644 src/bin/variable_bindings/mut.rs create mode 100644 src/bin/variable_bindings/scope.rs diff --git a/src/bin/attribute/cfg.rs b/src/bin/attribute/cfg.rs new file mode 100644 index 0000000..263ce64 --- /dev/null +++ b/src/bin/attribute/cfg.rs @@ -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(); +} + diff --git a/src/bin/attribute/cfg/custom.rs b/src/bin/attribute/cfg/custom.rs new file mode 100644 index 0000000..df97dcd --- /dev/null +++ b/src/bin/attribute/cfg/custom.rs @@ -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(); +} + diff --git a/src/bin/attribute/unused.rs b/src/bin/attribute/unused.rs new file mode 100644 index 0000000..e8401e7 --- /dev/null +++ b/src/bin/attribute/unused.rs @@ -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(); +} + diff --git a/src/bin/conversion/from_into.rs b/src/bin/conversion/from_into.rs new file mode 100644 index 0000000..5781070 --- /dev/null +++ b/src/bin/conversion/from_into.rs @@ -0,0 +1,46 @@ +// ./src/conversion/from_into.md + + +use std::convert::From; + +#[derive(Debug)] +struct Number { + value: i32, +} + +impl From 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 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(); +} + diff --git a/src/bin/conversion/string.rs b/src/bin/conversion/string.rs new file mode 100644 index 0000000..7c18813 --- /dev/null +++ b/src/bin/conversion/string.rs @@ -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::().unwrap(); + + let sum = parsed + turbo_parsed; + println!("Sum: {:?}", sum); +} + +pub fn main() { + part0(); + part1(); +} + diff --git a/src/bin/conversion/try_from_try_into.rs b/src/bin/conversion/try_from_try_into.rs new file mode 100644 index 0000000..9209283 --- /dev/null +++ b/src/bin/conversion/try_from_try_into.rs @@ -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 for EvenNumber { + type Error = (); + + fn try_from(value: i32) -> Result { + 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 = 8i32.try_into(); + assert_eq!(result, Ok(EvenNumber(8))); + let result: Result = 5i32.try_into(); + assert_eq!(result, Err(())); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/custom_types/constants.rs b/src/bin/custom_types/constants.rs new file mode 100644 index 0000000..89885e8 --- /dev/null +++ b/src/bin/custom_types/constants.rs @@ -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(); +} + diff --git a/src/bin/custom_types/enum.rs b/src/bin/custom_types/enum.rs new file mode 100644 index 0000000..de44467 --- /dev/null +++ b/src/bin/custom_types/enum.rs @@ -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(); +} + diff --git a/src/bin/custom_types/enum/c_like.rs b/src/bin/custom_types/enum/c_like.rs new file mode 100644 index 0000000..c23cc1a --- /dev/null +++ b/src/bin/custom_types/enum/c_like.rs @@ -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(); +} + diff --git a/src/bin/custom_types/enum/enum_use.rs b/src/bin/custom_types/enum/enum_use.rs new file mode 100644 index 0000000..3b5c20a --- /dev/null +++ b/src/bin/custom_types/enum/enum_use.rs @@ -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(); +} + diff --git a/src/bin/custom_types/enum/testcase_linked_list.rs b/src/bin/custom_types/enum/testcase_linked_list.rs new file mode 100644 index 0000000..ea48a46 --- /dev/null +++ b/src/bin/custom_types/enum/testcase_linked_list.rs @@ -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), + // 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(); +} + diff --git a/src/bin/custom_types/structs.rs b/src/bin/custom_types/structs.rs new file mode 100644 index 0000000..0b689d6 --- /dev/null +++ b/src/bin/custom_types/structs.rs @@ -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(); +} + diff --git a/src/bin/error/abort_unwind.rs b/src/bin/error/abort_unwind.rs new file mode 100644 index 0000000..13b441d --- /dev/null +++ b/src/bin/error/abort_unwind.rs @@ -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(); +} + diff --git a/src/bin/error/iter_result.rs b/src/bin/error/iter_result.rs new file mode 100644 index 0000000..c32e741 --- /dev/null +++ b/src/bin/error/iter_result.rs @@ -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::()) + .collect(); + println!("Results: {:?}", numbers); +} + +fn part1() { + let strings = vec!["tofu", "93", "18"]; + let numbers: Vec<_> = strings + .into_iter() + .filter_map(|s| s.parse::().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::()) + .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, _> = strings + .into_iter() + .map(|s| s.parse::()) + .collect(); + println!("Results: {:?}", numbers); +} + +fn part4() { + let strings = vec!["tofu", "93", "18"]; + let (numbers, errors): (Vec<_>, Vec<_>) = strings + .into_iter() + .map(|s| s.parse::()) + .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::()) + .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(); +} + diff --git a/src/bin/error/multiple_error_types.rs b/src/bin/error/multiple_error_types.rs new file mode 100644 index 0000000..9feb2fe --- /dev/null +++ b/src/bin/error/multiple_error_types.rs @@ -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::().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(); +} + diff --git a/src/bin/error/multiple_error_types/boxing_errors.rs b/src/bin/error/multiple_error_types/boxing_errors.rs new file mode 100644 index 0000000..a13b51d --- /dev/null +++ b/src/bin/error/multiple_error_types/boxing_errors.rs @@ -0,0 +1,51 @@ +// ./src/error/multiple_error_types/boxing_errors.md + + +use std::error; +use std::fmt; + +// Change the alias to `Box`. +type Result = std::result::Result>; + +#[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 { + vec.first() + .ok_or_else(|| EmptyVec.into()) // Converts to Box + .and_then(|s| { + s.parse::() + .map_err(|e| e.into()) // Converts to Box + .map(|i| 2 * i) + }) +} + +fn print(result: Result) { + 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(); +} + diff --git a/src/bin/error/multiple_error_types/define_error_type.rs b/src/bin/error/multiple_error_types/define_error_type.rs new file mode 100644 index 0000000..aa80ca5 --- /dev/null +++ b/src/bin/error/multiple_error_types/define_error_type.rs @@ -0,0 +1,57 @@ +// ./src/error/multiple_error_types/define_error_type.md + + +use std::fmt; + +type Result = std::result::Result; + +// 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 { + vec.first() + // Change the error to our new type. + .ok_or(DoubleError) + .and_then(|s| { + s.parse::() + // Update to the new error type here also. + .map_err(|_| DoubleError) + .map(|i| 2 * i) + }) +} + +fn print(result: Result) { + 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(); +} + diff --git a/src/bin/error/multiple_error_types/option_result.rs b/src/bin/error/multiple_error_types/option_result.rs new file mode 100644 index 0000000..7e5e702 --- /dev/null +++ b/src/bin/error/multiple_error_types/option_result.rs @@ -0,0 +1,50 @@ +// ./src/error/multiple_error_types/option_result.md + + +use std::num::ParseIntError; + +fn double_first(vec: Vec<&str>) -> Option> { + vec.first().map(|first| { + first.parse::().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, ParseIntError> { + let opt = vec.first().map(|first| { + first.parse::().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(); +} + diff --git a/src/bin/error/multiple_error_types/reenter_question_mark.rs b/src/bin/error/multiple_error_types/reenter_question_mark.rs new file mode 100644 index 0000000..38da9fe --- /dev/null +++ b/src/bin/error/multiple_error_types/reenter_question_mark.rs @@ -0,0 +1,49 @@ +// ./src/error/multiple_error_types/reenter_question_mark.md + + +use std::error; +use std::fmt; + +// Change the alias to `Box`. +type Result = std::result::Result>; + +#[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 { + let first = vec.first().ok_or(EmptyVec)?; + let parsed = first.parse::()?; + Ok(2 * parsed) +} + +fn print(result: Result) { + 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(); +} + diff --git a/src/bin/error/multiple_error_types/wrap_error.rs b/src/bin/error/multiple_error_types/wrap_error.rs new file mode 100644 index 0000000..dc5c0fe --- /dev/null +++ b/src/bin/error/multiple_error_types/wrap_error.rs @@ -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 = std::result::Result; + +#[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 for DoubleError { + fn from(err: ParseIntError) -> DoubleError { + DoubleError::Parse(err) + } +} + +fn double_first(vec: Vec<&str>) -> Result { + 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::()?; + + Ok(2 * parsed) +} + +fn print(result: Result) { + 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(); +} + diff --git a/src/bin/error/option_unwrap.rs b/src/bin/error/option_unwrap.rs new file mode 100644 index 0000000..e1996ff --- /dev/null +++ b/src/bin/error/option_unwrap.rs @@ -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(); +} + diff --git a/src/bin/error/option_unwrap/and_then.rs b/src/bin/error/option_unwrap/and_then.rs new file mode 100644 index 0000000..999e24d --- /dev/null +++ b/src/bin/error/option_unwrap/and_then.rs @@ -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 { + match food { + Food::Sushi => None, + _ => Some(food), + } +} + +// We have the recipe for everything except Cordon Bleu. +fn have_recipe(food: Food) -> Option { + 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 { + 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 { + 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(); +} + diff --git a/src/bin/error/option_unwrap/defaults.rs b/src/bin/error/option_unwrap/defaults.rs new file mode 100644 index 0000000..46acc89 --- /dev/null +++ b/src/bin/error/option_unwrap/defaults.rs @@ -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 = 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 = 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 = 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 = 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(); +} + diff --git a/src/bin/error/option_unwrap/map.rs b/src/bin/error/option_unwrap/map.rs new file mode 100644 index 0000000..94b07f1 --- /dev/null +++ b/src/bin/error/option_unwrap/map.rs @@ -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) -> Option { + 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) -> Option { + 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) -> Option { + 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) -> Option { + 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) { + 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(); +} + diff --git a/src/bin/error/option_unwrap/question_mark.rs b/src/bin/error/option_unwrap/question_mark.rs new file mode 100644 index 0000000..fcc6fa8 --- /dev/null +++ b/src/bin/error/option_unwrap/question_mark.rs @@ -0,0 +1,53 @@ +// ./src/error/option_unwrap/question_mark.md + + +fn next_birthday(current_age: Option) -> Option { + // 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, +} + +#[derive(Clone, Copy)] +struct Job { + phone_number: Option, +} + +#[derive(Clone, Copy)] +struct PhoneNumber { + area_code: Option, + 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 { + // 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(); +} + diff --git a/src/bin/error/panic.rs b/src/bin/error/panic.rs new file mode 100644 index 0000000..b8f9c2e --- /dev/null +++ b/src/bin/error/panic.rs @@ -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(); +} + diff --git a/src/bin/error/result.rs b/src/bin/error/result.rs new file mode 100644 index 0000000..b2939de --- /dev/null +++ b/src/bin/error/result.rs @@ -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::().unwrap(); + let second_number = second_number_str.parse::().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::() { + Ok(number) => number, + Err(e) => return Err(e), + }; + println!("{}", number); + Ok(()) +} + +pub fn main() { + part0(); + part1(); +} + diff --git a/src/bin/error/result/early_returns.rs b/src/bin/error/result/early_returns.rs new file mode 100644 index 0000000..6669677 --- /dev/null +++ b/src/bin/error/result/early_returns.rs @@ -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 { + let first_number = match first_number_str.parse::() { + Ok(first_number) => first_number, + Err(e) => return Err(e), + }; + + let second_number = match second_number_str.parse::() { + Ok(second_number) => second_number, + Err(e) => return Err(e), + }; + + Ok(first_number * second_number) +} + +fn print(result: Result) { + 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(); +} + diff --git a/src/bin/error/result/enter_question_mark.rs b/src/bin/error/result/enter_question_mark.rs new file mode 100644 index 0000000..b57e6c5 --- /dev/null +++ b/src/bin/error/result/enter_question_mark.rs @@ -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 { + let first_number = first_number_str.parse::()?; + let second_number = second_number_str.parse::()?; + + Ok(first_number * second_number) +} + +fn print(result: Result) { + 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 { + let first_number = try!(first_number_str.parse::()); + let second_number = try!(second_number_str.parse::()); + + Ok(first_number * second_number) +} + +fn print(result: Result) { + 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(); +} + diff --git a/src/bin/error/result/result_alias.rs b/src/bin/error/result/result_alias.rs new file mode 100644 index 0000000..902d43b --- /dev/null +++ b/src/bin/error/result/result_alias.rs @@ -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 = Result; + +// Use the above alias to refer to our specific `Result` type. +fn multiply(first_number_str: &str, second_number_str: &str) -> AliasedResult { + first_number_str.parse::().and_then(|first_number| { + second_number_str.parse::().map(|second_number| first_number * second_number) + }) +} + +// Here, the alias again allows us to save some space. +fn print(result: AliasedResult) { + 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(); +} + diff --git a/src/bin/error/result/result_map.rs b/src/bin/error/result/result_map.rs new file mode 100644 index 0000000..d335afb --- /dev/null +++ b/src/bin/error/result/result_map.rs @@ -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 { + match first_number_str.parse::() { + Ok(first_number) => { + match second_number_str.parse::() { + Ok(second_number) => { + Ok(first_number * second_number) + }, + Err(e) => Err(e), + } + }, + Err(e) => Err(e), + } +} + +fn print(result: Result) { + 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 { + first_number_str.parse::().and_then(|first_number| { + second_number_str.parse::().map(|second_number| first_number * second_number) + }) +} + +fn print(result: Result) { + 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(); +} + diff --git a/src/bin/example1.rs b/src/bin/example1.rs deleted file mode 100644 index 719e3da..0000000 --- a/src/bin/example1.rs +++ /dev/null @@ -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(); -} diff --git a/src/bin/example2.rs b/src/bin/example2.rs deleted file mode 100644 index 7b16354..0000000 --- a/src/bin/example2.rs +++ /dev/null @@ -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; -} diff --git a/src/bin/example3.rs b/src/bin/example3.rs deleted file mode 100644 index a0cd6fe..0000000 --- a/src/bin/example3.rs +++ /dev/null @@ -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(); -} diff --git a/src/bin/expression.rs b/src/bin/expression.rs new file mode 100644 index 0000000..e3f06b6 --- /dev/null +++ b/src/bin/expression.rs @@ -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(); +} + diff --git a/src/bin/flow_control/for.rs b/src/bin/flow_control/for.rs new file mode 100644 index 0000000..7c1d59d --- /dev/null +++ b/src/bin/flow_control/for.rs @@ -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(); +} + diff --git a/src/bin/flow_control/if_else.rs b/src/bin/flow_control/if_else.rs new file mode 100644 index 0000000..722497e --- /dev/null +++ b/src/bin/flow_control/if_else.rs @@ -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(); +} + diff --git a/src/bin/flow_control/if_let.rs b/src/bin/flow_control/if_let.rs new file mode 100644 index 0000000..0e0f4b8 --- /dev/null +++ b/src/bin/flow_control/if_let.rs @@ -0,0 +1,94 @@ +// ./src/flow_control/if_let.md + + +fn part0() { + // 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 :)!"); + } +} + +// 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(); +} + diff --git a/src/bin/flow_control/loop.rs b/src/bin/flow_control/loop.rs new file mode 100644 index 0000000..2691fec --- /dev/null +++ b/src/bin/flow_control/loop.rs @@ -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(); +} + diff --git a/src/bin/flow_control/loop/nested.rs b/src/bin/flow_control/loop/nested.rs new file mode 100644 index 0000000..66854e9 --- /dev/null +++ b/src/bin/flow_control/loop/nested.rs @@ -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(); +} + diff --git a/src/bin/flow_control/loop/return.rs b/src/bin/flow_control/loop/return.rs new file mode 100644 index 0000000..2a04891 --- /dev/null +++ b/src/bin/flow_control/loop/return.rs @@ -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(); +} + diff --git a/src/bin/flow_control/match.rs b/src/bin/flow_control/match.rs new file mode 100644 index 0000000..7d03210 --- /dev/null +++ b/src/bin/flow_control/match.rs @@ -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(); +} + diff --git a/src/bin/flow_control/match/binding.rs b/src/bin/flow_control/match/binding.rs new file mode 100644 index 0000000..667c724 --- /dev/null +++ b/src/bin/flow_control/match/binding.rs @@ -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 { + 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(); +} + diff --git a/src/bin/flow_control/match/destructuring/destructure_enum.rs b/src/bin/flow_control/match/destructuring/destructure_enum.rs new file mode 100644 index 0000000..55635b6 --- /dev/null +++ b/src/bin/flow_control/match/destructuring/destructure_enum.rs @@ -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(); +} + diff --git a/src/bin/flow_control/match/destructuring/destructure_pointers.rs b/src/bin/flow_control/match/destructuring/destructure_pointers.rs new file mode 100644 index 0000000..aad23f7 --- /dev/null +++ b/src/bin/flow_control/match/destructuring/destructure_pointers.rs @@ -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(); +} + diff --git a/src/bin/flow_control/match/destructuring/destructure_slice.rs b/src/bin/flow_control/match/destructuring/destructure_slice.rs new file mode 100644 index 0000000..422409b --- /dev/null +++ b/src/bin/flow_control/match/destructuring/destructure_slice.rs @@ -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(); +} + diff --git a/src/bin/flow_control/match/destructuring/destructure_structures.rs b/src/bin/flow_control/match/destructuring/destructure_structures.rs new file mode 100644 index 0000000..4451160 --- /dev/null +++ b/src/bin/flow_control/match/destructuring/destructure_structures.rs @@ -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(); +} + diff --git a/src/bin/flow_control/match/destructuring/destructure_tuple.rs b/src/bin/flow_control/match/destructuring/destructure_tuple.rs new file mode 100644 index 0000000..73c751c --- /dev/null +++ b/src/bin/flow_control/match/destructuring/destructure_tuple.rs @@ -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(); +} + diff --git a/src/bin/flow_control/match/guard.rs b/src/bin/flow_control/match/guard.rs new file mode 100644 index 0000000..49c814e --- /dev/null +++ b/src/bin/flow_control/match/guard.rs @@ -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(); +} + diff --git a/src/bin/flow_control/while.rs b/src/bin/flow_control/while.rs new file mode 100644 index 0000000..9a5a9da --- /dev/null +++ b/src/bin/flow_control/while.rs @@ -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(); +} + diff --git a/src/bin/flow_control/while_let.rs b/src/bin/flow_control/while_let.rs new file mode 100644 index 0000000..9436583 --- /dev/null +++ b/src/bin/flow_control/while_let.rs @@ -0,0 +1,28 @@ +// ./src/flow_control/while_let.md + + +fn part0() { + // 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. +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/fn.rs b/src/bin/fn.rs new file mode 100644 index 0000000..4582701 --- /dev/null +++ b/src/bin/fn.rs @@ -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(); +} + diff --git a/src/bin/fn/closures.rs b/src/bin/fn/closures.rs new file mode 100644 index 0000000..ae8fb93 --- /dev/null +++ b/src/bin/fn/closures.rs @@ -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(); +} + diff --git a/src/bin/fn/closures/anonymity.rs b/src/bin/fn/closures/anonymity.rs new file mode 100644 index 0000000..90a58a4 --- /dev/null +++ b/src/bin/fn/closures/anonymity.rs @@ -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) 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(); +} + diff --git a/src/bin/fn/closures/capture.rs b/src/bin/fn/closures/capture.rs new file mode 100644 index 0000000..9b5dade --- /dev/null +++ b/src/bin/fn/closures/capture.rs @@ -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(); +} + diff --git a/src/bin/fn/closures/closure_examples/iter_any.rs b/src/bin/fn/closures/closure_examples/iter_any.rs new file mode 100644 index 0000000..c611bdc --- /dev/null +++ b/src/bin/fn/closures/closure_examples/iter_any.rs @@ -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(); +} + diff --git a/src/bin/fn/closures/closure_examples/iter_find.rs b/src/bin/fn/closures/closure_examples/iter_find.rs new file mode 100644 index 0000000..4172971 --- /dev/null +++ b/src/bin/fn/closures/closure_examples/iter_find.rs @@ -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(); +} + diff --git a/src/bin/fn/closures/input_functions.rs b/src/bin/fn/closures/input_functions.rs new file mode 100644 index 0000000..2c17ff8 --- /dev/null +++ b/src/bin/fn/closures/input_functions.rs @@ -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: 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(); +} + diff --git a/src/bin/fn/closures/input_parameters.rs b/src/bin/fn/closures/input_parameters.rs new file mode 100644 index 0000000..45112e5 --- /dev/null +++ b/src/bin/fn/closures/input_parameters.rs @@ -0,0 +1,59 @@ +// ./src/fn/closures/input_parameters.md + + +// A function which takes a closure as an argument and calls it. +// denotes that F is a "Generic type parameter" +fn apply(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) -> 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(); +} + diff --git a/src/bin/fn/closures/output_parameters.rs b/src/bin/fn/closures/output_parameters.rs new file mode 100644 index 0000000..f08ba34 --- /dev/null +++ b/src/bin/fn/closures/output_parameters.rs @@ -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(); +} + diff --git a/src/bin/fn/hof.rs b/src/bin/fn/hof.rs new file mode 100644 index 0000000..9db11cc --- /dev/null +++ b/src/bin/fn/hof.rs @@ -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(); +} + diff --git a/src/bin/fn/methods.rs b/src/bin/fn/methods.rs new file mode 100644 index 0000000..7c5e819 --- /dev/null +++ b/src/bin/fn/methods.rs @@ -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, Box); + +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(); +} + diff --git a/src/bin/generics.rs b/src/bin/generics.rs new file mode 100644 index 0000000..cf6c80c --- /dev/null +++ b/src/bin/generics.rs @@ -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 ``. +// 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, `` 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); + +fn part0() { + // `Single` is concrete and explicitly takes `A`. + let _s = Single(A); + + // Create a variable `_char` of type `SingleGen` + // and give it the value `SingleGen('a')`. + // Here, `SingleGen` has a type parameter explicitly specified. + let _char: SingleGen = 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(); +} + diff --git a/src/bin/generics/assoc_items/the_problem.rs b/src/bin/generics/assoc_items/the_problem.rs new file mode 100644 index 0000000..9e6e2e3 --- /dev/null +++ b/src/bin/generics/assoc_items/the_problem.rs @@ -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 { + 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 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(container: &C) -> i32 where + C: Contains { + 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(); +} + diff --git a/src/bin/generics/assoc_items/types.rs b/src/bin/generics/assoc_items/types.rs new file mode 100644 index 0000000..bca5d0f --- /dev/null +++ b/src/bin/generics/assoc_items/types.rs @@ -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(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(); +} + diff --git a/src/bin/generics/bounds.rs b/src/bin/generics/bounds.rs new file mode 100644 index 0000000..0b93d54 --- /dev/null +++ b/src/bin/generics/bounds.rs @@ -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: &T) { + println!("{:?}", t); +} + +// `T` must implement `HasArea`. Any type which meets +// the bound can access `HasArea`'s function `area`. +fn area(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(); +} + diff --git a/src/bin/generics/bounds/testcase_empty.rs b/src/bin/generics/bounds/testcase_empty.rs new file mode 100644 index 0000000..17d5bb5 --- /dev/null +++ b/src/bin/generics/bounds/testcase_empty.rs @@ -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) -> &'static str { "red" } +fn 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(); +} + diff --git a/src/bin/generics/gen_fn.rs b/src/bin/generics/gen_fn.rs new file mode 100644 index 0000000..6193ed6 --- /dev/null +++ b/src/bin/generics/gen_fn.rs @@ -0,0 +1,45 @@ +// ./src/generics/gen_fn.md + + +struct A; // Concrete type `A`. +struct S(A); // Concrete type `S`. +struct SGen(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 `` 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`. +// 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) {} + +// Define a function `gen_spec_i32` that takes an argument `_s` of type `SGen`. +// 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) {} + +// Define a function `generic` that takes an argument `_s` of type `SGen`. +// Because `SGen` is preceded by ``, this function is generic over `T`. +fn generic(_s: SGen) {} + +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::(SGen('a')); + + // Implicitly specified type parameter `char` to `generic()`. + generic(SGen('c')); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/generics/gen_trait.rs b/src/bin/generics/gen_trait.rs new file mode 100644 index 0000000..33d6753 --- /dev/null +++ b/src/bin/generics/gen_trait.rs @@ -0,0 +1,38 @@ +// ./src/generics/gen_trait.md + + +// Non-copyable types. +struct Empty; +struct Null; + +// A trait generic over `T`. +trait DoubleDrop { + // 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` for any generic parameter `T` and +// caller `U`. +impl DoubleDrop 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(); +} + diff --git a/src/bin/generics/impl.rs b/src/bin/generics/impl.rs new file mode 100644 index 0000000..6135030 --- /dev/null +++ b/src/bin/generics/impl.rs @@ -0,0 +1,36 @@ +// ./src/generics/impl.md + + +struct Val { + val: f64, +} + +struct GenVal { + gen_val: T, +} + +// impl of Val +impl Val { + fn value(&self) -> &f64 { + &self.val + } +} + +// impl of GenVal for a generic type `T` +impl GenVal { + 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(); +} + diff --git a/src/bin/generics/multi_bounds.rs b/src/bin/generics/multi_bounds.rs new file mode 100644 index 0000000..6ed9f57 --- /dev/null +++ b/src/bin/generics/multi_bounds.rs @@ -0,0 +1,31 @@ +// ./src/generics/multi_bounds.md + + +use std::fmt::{Debug, Display}; + +fn compare_prints(t: &T) { + println!("Debug: `{:?}`", t); + println!("Display: `{}`", t); +} + +fn compare_types(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(); +} + diff --git a/src/bin/generics/phantom.rs b/src/bin/generics/phantom.rs new file mode 100644 index 0000000..fd4bb83 --- /dev/null +++ b/src/bin/generics/phantom.rs @@ -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, PhantomData); + +// A phantom type struct which is generic over `A` with hidden parameter `B`. +#[derive(PartialEq)] // Allow equality test for this type. +struct PhantomStruct { first: A, phantom: PhantomData } + +// 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 ``. + let _tuple1: PhantomTuple = PhantomTuple('Q', PhantomData); + // PhantomTuple type specified as ``. + let _tuple2: PhantomTuple = PhantomTuple('Q', PhantomData); + + // Type specified as ``. + let _struct1: PhantomStruct = PhantomStruct { + first: 'Q', + phantom: PhantomData, + }; + // Type specified as ``. + let _struct2: PhantomStruct = 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(); +} + diff --git a/src/bin/generics/phantom/testcase_units.rs b/src/bin/generics/phantom/testcase_units.rs new file mode 100644 index 0000000..0a237ed --- /dev/null +++ b/src/bin/generics/phantom/testcase_units.rs @@ -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(f64, PhantomData); + +/// The `Add` trait defines the behavior of the `+` operator. +impl Add for Length { + type Output = Length; + + // add() returns a new `Length` struct containing the sum. + fn add(self, rhs: Length) -> Length { + // `+` 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 = Length(12.0, PhantomData); + // `one_meter` has phantom type parameter `Mm`. + let one_meter: Length = Length(1000.0, PhantomData); + + // `+` calls the `add()` method we implemented for `Length`. + // + // 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(); +} + diff --git a/src/bin/generics/where.rs b/src/bin/generics/where.rs new file mode 100644 index 0000000..84d26f7 --- /dev/null +++ b/src/bin/generics/where.rs @@ -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 PrintInOption for T where + Option: Debug { + // We want `Option: 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(); +} + diff --git a/src/bin/hello_world.rs b/src/bin/hello.rs similarity index 83% rename from src/bin/hello_world.rs rename to src/bin/hello.rs index ae92592..6bf11a0 100644 --- a/src/bin/hello_world.rs +++ b/src/bin/hello.rs @@ -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(); } diff --git a/src/bin/hello/comment.rs b/src/bin/hello/comment.rs new file mode 100644 index 0000000..37e12df --- /dev/null +++ b/src/bin/hello/comment.rs @@ -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(); +} + diff --git a/src/bin/hello/print.rs b/src/bin/hello/print.rs new file mode 100644 index 0000000..7c25404 --- /dev/null +++ b/src/bin/hello/print.rs @@ -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(); +} + diff --git a/src/bin/hello/print/fmt.rs b/src/bin/hello/print/fmt.rs new file mode 100644 index 0000000..c4d20e9 --- /dev/null +++ b/src/bin/hello/print/fmt.rs @@ -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(); +} + diff --git a/src/bin/hello/print/print_debug.rs b/src/bin/hello/print/print_debug.rs new file mode 100644 index 0000000..b843bde --- /dev/null +++ b/src/bin/hello/print/print_debug.rs @@ -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(); +} + diff --git a/src/bin/hello/print/print_display.rs b/src/bin/hello/print/print_display.rs new file mode 100644 index 0000000..65f0033 --- /dev/null +++ b/src/bin/hello/print/print_display.rs @@ -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(); +} + diff --git a/src/bin/hello/print/print_display/testcase_list.rs b/src/bin/hello/print/print_display/testcase_list.rs new file mode 100644 index 0000000..eb4f431 --- /dev/null +++ b/src/bin/hello/print/print_display/testcase_list.rs @@ -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); + +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(); +} + diff --git a/src/bin/macros.rs b/src/bin/macros.rs new file mode 100644 index 0000000..71ce069 --- /dev/null +++ b/src/bin/macros.rs @@ -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(); +} + diff --git a/src/bin/macros/designators.rs b/src/bin/macros/designators.rs new file mode 100644 index 0000000..bfcc256 --- /dev/null +++ b/src/bin/macros/designators.rs @@ -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(); +} + diff --git a/src/bin/macros/dsl.rs b/src/bin/macros/dsl.rs new file mode 100644 index 0000000..9874779 --- /dev/null +++ b/src/bin/macros/dsl.rs @@ -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(); +} + diff --git a/src/bin/macros/overload.rs b/src/bin/macros/overload.rs new file mode 100644 index 0000000..b723bde --- /dev/null +++ b/src/bin/macros/overload.rs @@ -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(); +} + diff --git a/src/bin/macros/repeat.rs b/src/bin/macros/repeat.rs new file mode 100644 index 0000000..e06f3e0 --- /dev/null +++ b/src/bin/macros/repeat.rs @@ -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(); +} + diff --git a/src/bin/macros/variadics.rs b/src/bin/macros/variadics.rs new file mode 100644 index 0000000..3500579 --- /dev/null +++ b/src/bin/macros/variadics.rs @@ -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(); +} + diff --git a/src/bin/meta/doc.rs b/src/bin/meta/doc.rs new file mode 100644 index 0000000..2a91720 --- /dev/null +++ b/src/bin/meta/doc.rs @@ -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(); +} + diff --git a/src/bin/meta/playground.rs b/src/bin/meta/playground.rs new file mode 100644 index 0000000..9eabd19 --- /dev/null +++ b/src/bin/meta/playground.rs @@ -0,0 +1,11 @@ +// ./src/meta/playground.md + + +fn part0() { + println!("Hello World!"); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/mod/struct_visibility.rs b/src/bin/mod/struct_visibility.rs new file mode 100644 index 0000000..0ad25b4 --- /dev/null +++ b/src/bin/mod/struct_visibility.rs @@ -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 { + pub contents: T, + } + + // A public struct with a private field of generic type `T` + #[allow(dead_code)] + pub struct ClosedBox { + contents: T, + } + + impl ClosedBox { + // A public constructor method + pub fn new(contents: T) -> ClosedBox { + 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(); +} + diff --git a/src/bin/mod/super.rs b/src/bin/mod/super.rs new file mode 100644 index 0000000..97a227c --- /dev/null +++ b/src/bin/mod/super.rs @@ -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(); +} + diff --git a/src/bin/mod/use.rs b/src/bin/mod/use.rs new file mode 100644 index 0000000..3c9d746 --- /dev/null +++ b/src/bin/mod/use.rs @@ -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(); +} + diff --git a/src/bin/mod/visibility.rs b/src/bin/mod/visibility.rs new file mode 100644 index 0000000..cbad048 --- /dev/null +++ b/src/bin/mod/visibility.rs @@ -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(); +} + diff --git a/src/bin/primitives.rs b/src/bin/primitives.rs new file mode 100644 index 0000000..2c11693 --- /dev/null +++ b/src/bin/primitives.rs @@ -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(); +} + diff --git a/src/bin/primitives/array.rs b/src/bin/primitives/array.rs new file mode 100644 index 0000000..4af9f33 --- /dev/null +++ b/src/bin/primitives/array.rs @@ -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(); +} + diff --git a/src/bin/primitives/literals.rs b/src/bin/primitives/literals.rs new file mode 100644 index 0000000..2812b98 --- /dev/null +++ b/src/bin/primitives/literals.rs @@ -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(); +} + diff --git a/src/bin/primitives/tuples.rs b/src/bin/primitives/tuples.rs new file mode 100644 index 0000000..88bdd76 --- /dev/null +++ b/src/bin/primitives/tuples.rs @@ -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(); +} + diff --git a/src/bin/printf-nostd.rs b/src/bin/printf-nostd.rs deleted file mode 100644 index 83ced8d..0000000 --- a/src/bin/printf-nostd.rs +++ /dev/null @@ -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 {} -} diff --git a/src/bin/scope/borrow.rs b/src/bin/scope/borrow.rs new file mode 100644 index 0000000..c5e9377 --- /dev/null +++ b/src/bin/scope/borrow.rs @@ -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) { + 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(); +} + diff --git a/src/bin/scope/borrow/alias.rs b/src/bin/scope/borrow/alias.rs new file mode 100644 index 0000000..002fc98 --- /dev/null +++ b/src/bin/scope/borrow/alias.rs @@ -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(); +} + diff --git a/src/bin/scope/borrow/mut.rs b/src/bin/scope/borrow/mut.rs new file mode 100644 index 0000000..b96f929 --- /dev/null +++ b/src/bin/scope/borrow/mut.rs @@ -0,0 +1,53 @@ +// ./src/scope/borrow/mut.md + + +#[allow(dead_code)] +#[derive(Clone, Copy)] +struct Book { + // `&'static str` is a reference to a string allocated in read only memory + author: &'static str, + title: &'static str, + year: u32, +} + +// This function takes a reference to a book +fn borrow_book(book: &Book) { + println!("I immutably borrowed {} - {} edition", book.title, book.year); +} + +// This function takes a reference to a mutable book and changes `year` to 2014 +fn new_edition(book: &mut Book) { + book.year = 2014; + println!("I mutably borrowed {} - {} edition", book.title, book.year); +} + +fn part0() { + // Create an immutable Book named `immutabook` + let immutabook = Book { + // string literals have type `&'static str` + author: "Douglas Hofstadter", + title: "Gödel, Escher, Bach", + year: 1979, + }; + + // Create a mutable copy of `immutabook` and call it `mutabook` + let mut mutabook = immutabook; + + // Immutably borrow an immutable object + borrow_book(&immutabook); + + // Immutably borrow a mutable object + borrow_book(&mutabook); + + // Borrow a mutable object as mutable + new_edition(&mut mutabook); + + // Error! Cannot borrow an immutable object as mutable +// new_edition(&mut immutabook); + // FIXME ^ Comment out this line +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/scope/borrow/ref.rs b/src/bin/scope/borrow/ref.rs new file mode 100644 index 0000000..e83cbb8 --- /dev/null +++ b/src/bin/scope/borrow/ref.rs @@ -0,0 +1,57 @@ +// ./src/scope/borrow/ref.md + + +#[derive(Clone, Copy)] +struct Point { x: i32, y: i32 } + +fn part0() { + let c = 'Q'; + + // A `ref` borrow on the left side of an assignment is equivalent to + // an `&` borrow on the right side. + let ref ref_c1 = c; + let ref_c2 = &c; + + println!("ref_c1 equals ref_c2: {}", *ref_c1 == *ref_c2); + + let point = Point { x: 0, y: 0 }; + + // `ref` is also valid when destructuring a struct. + let _copy_of_x = { + // `ref_to_x` is a reference to the `x` field of `point`. + let Point { x: ref ref_to_x, y: _ } = point; + + // Return a copy of the `x` field of `point`. + *ref_to_x + }; + + // A mutable copy of `point` + let mut mutable_point = point; + + { + // `ref` can be paired with `mut` to take mutable references. + let Point { x: _, y: ref mut mut_ref_to_y } = mutable_point; + + // Mutate the `y` field of `mutable_point` via a mutable reference. + *mut_ref_to_y = 1; + } + + println!("point is ({}, {})", point.x, point.y); + println!("mutable_point is ({}, {})", mutable_point.x, mutable_point.y); + + // A mutable tuple that includes a pointer + let mut mutable_tuple = (Box::new(5u32), 3u32); + + { + // Destructure `mutable_tuple` to change the value of `last`. + let (_, ref mut last) = mutable_tuple; + *last = 2u32; + } + + println!("tuple is {:?}", mutable_tuple); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/scope/lifetime.rs b/src/bin/scope/lifetime.rs new file mode 100644 index 0000000..9ade8dc --- /dev/null +++ b/src/bin/scope/lifetime.rs @@ -0,0 +1,30 @@ +// ./src/scope/lifetime.md + + +// Lifetimes are annotated below with lines denoting the creation +// and destruction of each variable. +// `i` has the longest lifetime because its scope entirely encloses +// both `borrow1` and `borrow2`. The duration of `borrow1` compared +// to `borrow2` is irrelevant since they are disjoint. +fn part0() { + let i = 3; // Lifetime for `i` starts. ────────────────┐ + // │ + { // │ + let borrow1 = &i; // `borrow1` lifetime starts. ──┐│ + // ││ + println!("borrow1: {}", borrow1); // ││ + } // `borrow1 ends. ──────────────────────────────────┘│ + // │ + // │ + { // │ + let borrow2 = &i; // `borrow2` lifetime starts. ──┐│ + // ││ + println!("borrow2: {}", borrow2); // ││ + } // `borrow2` ends. ─────────────────────────────────┘│ + // │ +} // Lifetime ends. ─────────────────────────────────────┘ + +pub fn main() { + part0(); +} + diff --git a/src/bin/scope/lifetime/elision.rs b/src/bin/scope/lifetime/elision.rs new file mode 100644 index 0000000..09a1e39 --- /dev/null +++ b/src/bin/scope/lifetime/elision.rs @@ -0,0 +1,33 @@ +// ./src/scope/lifetime/elision.md + + +// `elided_input` and `annotated_input` essentially have identical signatures +// because the lifetime of `elided_input` is inferred by the compiler: +fn elided_input(x: &i32) { + println!("`elided_input`: {}", x); +} + +fn annotated_input<'a>(x: &'a i32) { + println!("`annotated_input`: {}", x); +} + +// Similarly, `elided_pass` and `annotated_pass` have identical signatures +// because the lifetime is added implicitly to `elided_pass`: +fn elided_pass(x: &i32) -> &i32 { x } + +fn annotated_pass<'a>(x: &'a i32) -> &'a i32 { x } + +fn part0() { + let x = 3; + + elided_input(&x); + annotated_input(&x); + + println!("`elided_pass`: {}", elided_pass(&x)); + println!("`annotated_pass`: {}", annotated_pass(&x)); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/scope/lifetime/explicit.rs b/src/bin/scope/lifetime/explicit.rs new file mode 100644 index 0000000..3b35080 --- /dev/null +++ b/src/bin/scope/lifetime/explicit.rs @@ -0,0 +1,41 @@ +// ./src/scope/lifetime/explicit.md + + +// `print_refs` takes two references to `i32` which have different +// lifetimes `'a` and `'b`. These two lifetimes must both be at +// least as long as the function `print_refs`. +fn print_refs<'a, 'b>(x: &'a i32, y: &'b i32) { + println!("x is {} and y is {}", x, y); +} + +// A function which takes no arguments, but has a lifetime parameter `'a`. +fn failed_borrow<'a>() { + let _x = 12; + + // ERROR: `_x` does not live long enough + let y: &'a i32 = &_x; + // Attempting to use the lifetime `'a` as an explicit type annotation + // inside the function will fail because the lifetime of `&_x` is shorter + // than that of `y`. A short lifetime cannot be coerced into a longer one. +} + +fn part0() { + // Create variables to be borrowed below. + let (four, nine) = (4, 9); + + // Borrows (`&`) of both variables are passed into the function. + print_refs(&four, &nine); + // Any input which is borrowed must outlive the borrower. + // In other words, the lifetime of `four` and `nine` must + // be longer than that of `print_refs`. + + failed_borrow(); + // `failed_borrow` contains no references to force `'a` to be + // longer than the lifetime of the function, but `'a` is longer. + // Because the lifetime is never constrained, it defaults to `'static`. +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/scope/lifetime/fn.rs b/src/bin/scope/lifetime/fn.rs new file mode 100644 index 0000000..7256992 --- /dev/null +++ b/src/bin/scope/lifetime/fn.rs @@ -0,0 +1,50 @@ +// ./src/scope/lifetime/fn.md + + +// One input reference with lifetime `'a` which must live +// at least as long as the function. +fn print_one<'a>(x: &'a i32) { + println!("`print_one`: x is {}", x); +} + +// Mutable references are possible with lifetimes as well. +fn add_one<'a>(x: &'a mut i32) { + *x += 1; +} + +// Multiple elements with different lifetimes. In this case, it +// would be fine for both to have the same lifetime `'a`, but +// in more complex cases, different lifetimes may be required. +fn print_multi<'a, 'b>(x: &'a i32, y: &'b i32) { + println!("`print_multi`: x is {}, y is {}", x, y); +} + +// Returning references that have been passed in is acceptable. +// However, the correct lifetime must be returned. +fn pass_x<'a, 'b>(x: &'a i32, _: &'b i32) -> &'a i32 { x } + +//fn invalid_output<'a>() -> &'a String { &String::from("foo") } +// The above is invalid: `'a` must live longer than the function. +// Here, `&String::from("foo")` would create a `String`, followed by a +// reference. Then the data is dropped upon exiting the scope, leaving +// a reference to invalid data to be returned. + +fn part0() { + let x = 7; + let y = 9; + + print_one(&x); + print_multi(&x, &y); + + let z = pass_x(&x, &y); + print_one(z); + + let mut t = 3; + add_one(&mut t); + print_one(&t); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/scope/lifetime/lifetime_bounds.rs b/src/bin/scope/lifetime/lifetime_bounds.rs new file mode 100644 index 0000000..ac93499 --- /dev/null +++ b/src/bin/scope/lifetime/lifetime_bounds.rs @@ -0,0 +1,38 @@ +// ./src/scope/lifetime/lifetime_bounds.md + + +use std::fmt::Debug; // Trait to bound with. + +#[derive(Debug)] +struct Ref<'a, T: 'a>(&'a T); +// `Ref` contains a reference to a generic type `T` that has +// an unknown lifetime `'a`. `T` is bounded such that any +// *references* in `T` must outlive `'a`. Additionally, the lifetime +// of `Ref` may not exceed `'a`. + +// A generic function which prints using the `Debug` trait. +fn print(t: T) where + T: Debug { + println!("`print`: t is {:?}", t); +} + +// Here a reference to `T` is taken where `T` implements +// `Debug` and all *references* in `T` outlive `'a`. In +// addition, `'a` must outlive the function. +fn print_ref<'a, T>(t: &'a T) where + T: Debug + 'a { + println!("`print_ref`: t is {:?}", t); +} + +fn part0() { + let x = 7; + let ref_x = Ref(&x); + + print_ref(&ref_x); + print(ref_x); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/scope/lifetime/lifetime_coercion.rs b/src/bin/scope/lifetime/lifetime_coercion.rs new file mode 100644 index 0000000..0f15d03 --- /dev/null +++ b/src/bin/scope/lifetime/lifetime_coercion.rs @@ -0,0 +1,30 @@ +// ./src/scope/lifetime/lifetime_coercion.md + + +// Here, Rust infers a lifetime that is as short as possible. +// The two references are then coerced to that lifetime. +fn multiply<'a>(first: &'a i32, second: &'a i32) -> i32 { + first * second +} + +// `<'a: 'b, 'b>` reads as lifetime `'a` is at least as long as `'b`. +// Here, we take in an `&'a i32` and return a `&'b i32` as a result of coercion. +fn choose_first<'a: 'b, 'b>(first: &'a i32, _: &'b i32) -> &'b i32 { + first +} + +fn part0() { + let first = 2; // Longer lifetime + + { + let second = 3; // Shorter lifetime + + println!("The product is {}", multiply(&first, &second)); + println!("{} is the first", choose_first(&first, &second)); + }; +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/scope/lifetime/methods.rs b/src/bin/scope/lifetime/methods.rs new file mode 100644 index 0000000..7595854 --- /dev/null +++ b/src/bin/scope/lifetime/methods.rs @@ -0,0 +1,24 @@ +// ./src/scope/lifetime/methods.md + + +struct Owner(i32); + +impl Owner { + // Annotate lifetimes as in a standalone function. + fn add_one<'a>(&'a mut self) { self.0 += 1; } + fn print<'a>(&'a self) { + println!("`print`: {}", self.0); + } +} + +fn part0() { + let mut owner = Owner(18); + + owner.add_one(); + owner.print(); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/scope/lifetime/static_lifetime.rs b/src/bin/scope/lifetime/static_lifetime.rs new file mode 100644 index 0000000..04dec08 --- /dev/null +++ b/src/bin/scope/lifetime/static_lifetime.rs @@ -0,0 +1,23 @@ +// ./src/scope/lifetime/static_lifetime.md + + +use std::fmt::Debug; + +fn print_it( input: impl Debug + 'static ) { + println!( "'static value passed in is: {:?}", input ); +} + +fn part0() { + // i is owned and contains no references, thus it's 'static: + let i = 5; + print_it(i); + + // oops, &i only has the lifetime defined by the scope of + // part0(), so it's not 'static: + print_it(&i); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/scope/lifetime/struct.rs b/src/bin/scope/lifetime/struct.rs new file mode 100644 index 0000000..1b8db8d --- /dev/null +++ b/src/bin/scope/lifetime/struct.rs @@ -0,0 +1,41 @@ +// ./src/scope/lifetime/struct.md + + +// A type `Borrowed` which houses a reference to an +// `i32`. The reference to `i32` must outlive `Borrowed`. +#[derive(Debug)] +struct Borrowed<'a>(&'a i32); + +// Similarly, both references here must outlive this structure. +#[derive(Debug)] +struct NamedBorrowed<'a> { + x: &'a i32, + y: &'a i32, +} + +// An enum which is either an `i32` or a reference to one. +#[derive(Debug)] +enum Either<'a> { + Num(i32), + Ref(&'a i32), +} + +fn part0() { + let x = 18; + let y = 15; + + let single = Borrowed(&x); + let double = NamedBorrowed { x: &x, y: &y }; + let reference = Either::Ref(&x); + let number = Either::Num(y); + + println!("x is borrowed in {:?}", single); + println!("x and y are borrowed in {:?}", double); + println!("x is borrowed in {:?}", reference); + println!("y is *not* borrowed in {:?}", number); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/scope/lifetime/trait.rs b/src/bin/scope/lifetime/trait.rs new file mode 100644 index 0000000..0beca5a --- /dev/null +++ b/src/bin/scope/lifetime/trait.rs @@ -0,0 +1,27 @@ +// ./src/scope/lifetime/trait.md + + +// A struct with annotation of lifetimes. +#[derive(Debug)] +struct Borrowed<'a> { + x: &'a i32, +} + +// Annotate lifetimes to impl. +impl<'a> Default for Borrowed<'a> { + fn default() -> Self { + Self { + x: &10, + } + } +} + +fn part0() { + let b: Borrowed = Default::default(); + println!("b is {:?}", b); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/scope/move.rs b/src/bin/scope/move.rs new file mode 100644 index 0000000..5b41319 --- /dev/null +++ b/src/bin/scope/move.rs @@ -0,0 +1,50 @@ +// ./src/scope/move.md + + +// This function takes ownership of the heap allocated memory +fn destroy_box(c: Box) { + println!("Destroying a box that contains {}", c); + + // `c` is destroyed and the memory freed +} + +fn part0() { + // _Stack_ allocated integer + let x = 5u32; + + // *Copy* `x` into `y` - no resources are moved + let y = x; + + // Both values can be independently used + println!("x is {}, and y is {}", x, y); + + // `a` is a pointer to a _heap_ allocated integer + let a = Box::new(5i32); + + println!("a contains: {}", a); + + // *Move* `a` into `b` + let b = a; + // The pointer address of `a` is copied (not the data) into `b`. + // Both are now pointers to the same heap allocated data, but + // `b` now owns it. + + // Error! `a` can no longer access the data, because it no longer owns the + // heap memory + //println!("a contains: {}", a); + // TODO ^ Try uncommenting this line + + // This function takes ownership of the heap allocated memory from `b` + destroy_box(b); + + // Since the heap memory has been freed at this point, this action would + // result in dereferencing freed memory, but it's forbidden by the compiler + // Error! Same reason as the previous Error + //println!("b contains: {}", b); + // TODO ^ Try uncommenting this line +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/scope/move/mut.rs b/src/bin/scope/move/mut.rs new file mode 100644 index 0000000..f6b01e7 --- /dev/null +++ b/src/bin/scope/move/mut.rs @@ -0,0 +1,26 @@ +// ./src/scope/move/mut.md + + +fn part0() { + let immutable_box = Box::new(5u32); + + println!("immutable_box contains {}", immutable_box); + + // Mutability error + //*immutable_box = 4; + + // *Move* the box, changing the ownership (and mutability) + let mut mutable_box = immutable_box; + + println!("mutable_box contains {}", mutable_box); + + // Modify the contents of the box + *mutable_box = 4; + + println!("mutable_box now contains {}", mutable_box); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/scope/move/partial_move.rs b/src/bin/scope/move/partial_move.rs new file mode 100644 index 0000000..cd6915b --- /dev/null +++ b/src/bin/scope/move/partial_move.rs @@ -0,0 +1,33 @@ +// ./src/scope/move/partial_move.md + + +fn part0() { + #[derive(Debug)] + struct Person { + name: String, + age: Box, + } + + let person = Person { + name: String::from("Alice"), + age: Box::new(20), + }; + + // `name` is moved out of person, but `age` is referenced + let Person { name, ref age } = person; + + println!("The person's age is {}", age); + + println!("The person's name is {}", name); + + // Error! borrow of partially moved value: `person` partial move occurs + //println!("The person struct is {:?}", person); + + // `person` cannot be used but `person.age` can be used as it is not moved + println!("The person's age from person struct is {}", person.age); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/scope/raii.rs b/src/bin/scope/raii.rs new file mode 100644 index 0000000..66b7ae3 --- /dev/null +++ b/src/bin/scope/raii.rs @@ -0,0 +1,50 @@ +// ./src/scope/raii.md + + +// raii.rs +fn create_box() { + // Allocate an integer on the heap + let _box1 = Box::new(3i32); + + // `_box1` is destroyed here, and memory gets freed +} + +fn part0() { + // Allocate an integer on the heap + let _box2 = Box::new(5i32); + + // A nested scope: + { + // Allocate an integer on the heap + let _box3 = Box::new(4i32); + + // `_box3` is destroyed here, and memory gets freed + } + + // Creating lots of boxes just for fun + // There's no need to manually free memory! + for _ in 0u32..1_000 { + create_box(); + } + + // `_box2` is destroyed here, and memory gets freed +} + +struct ToDrop; + +impl Drop for ToDrop { + fn drop(&mut self) { + println!("ToDrop is being dropped"); + } +} + +fn part1() { + let x = ToDrop; + println!("Made a ToDrop!"); +} + +pub fn main() { + part0(); + part1(); +} + diff --git a/src/bin/std/arc.rs b/src/bin/std/arc.rs new file mode 100644 index 0000000..caefc89 --- /dev/null +++ b/src/bin/std/arc.rs @@ -0,0 +1,31 @@ +// ./src/std/arc.md + + +use std::time::Duration; +use std::sync::Arc; +use std::thread; + +fn part0() { + // This variable declaration is where its value is specified. + let apple = Arc::new("the same apple"); + + for _ in 0..10 { + // Here there is no value specification as it is a pointer to a + // reference in the memory heap. + let apple = Arc::clone(&apple); + + thread::spawn(move || { + // As Arc was used, threads can be spawned using the value allocated + // in the Arc variable pointer's location. + println!("{:?}", apple); + }); + } + + // Make sure all Arc instances are printed from spawned threads. + thread::sleep(Duration::from_secs(1)); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/std/box.rs b/src/bin/std/box.rs new file mode 100644 index 0000000..1204ddc --- /dev/null +++ b/src/bin/std/box.rs @@ -0,0 +1,73 @@ +// ./src/std/box.md + + +use std::mem; + +#[allow(dead_code)] +#[derive(Debug, Clone, Copy)] +struct Point { + x: f64, + y: f64, +} + +// A Rectangle can be specified by where its top left and bottom right +// corners are in space +#[allow(dead_code)] +struct Rectangle { + top_left: Point, + bottom_right: Point, +} + +fn origin() -> Point { + Point { x: 0.0, y: 0.0 } +} + +fn boxed_origin() -> Box { + // Allocate this point on the heap, and return a pointer to it + Box::new(Point { x: 0.0, y: 0.0 }) +} + +fn part0() { + // (all the type annotations are superfluous) + // Stack allocated variables + let point: Point = origin(); + let rectangle: Rectangle = Rectangle { + top_left: origin(), + bottom_right: Point { x: 3.0, y: -4.0 } + }; + + // Heap allocated rectangle + let boxed_rectangle: Box = Box::new(Rectangle { + top_left: origin(), + bottom_right: Point { x: 3.0, y: -4.0 }, + }); + + // The output of functions can be boxed + let boxed_point: Box = Box::new(origin()); + + // Double indirection + let box_in_a_box: Box> = Box::new(boxed_origin()); + + println!("Point occupies {} bytes on the stack", + mem::size_of_val(&point)); + println!("Rectangle occupies {} bytes on the stack", + mem::size_of_val(&rectangle)); + + // box size == pointer size + println!("Boxed point occupies {} bytes on the heap", + mem::size_of_val(&boxed_point)); + println!("Boxed rectangle occupies {} bytes on the heap", + mem::size_of_val(&boxed_rectangle)); + println!("Boxed box occupies {} bytes on the heap", + mem::size_of_val(&box_in_a_box)); + + // Copy the data contained in `boxed_point` into `unboxed_point` + let unboxed_point: Point = *boxed_point; + println!("Unboxed point occupies {} bytes on the stack", + mem::size_of_val(&unboxed_point)); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/std/hash.rs b/src/bin/std/hash.rs new file mode 100644 index 0000000..28b5808 --- /dev/null +++ b/src/bin/std/hash.rs @@ -0,0 +1,51 @@ +// ./src/std/hash.md + + +use std::collections::HashMap; + +fn call(number: &str) -> &str { + match number { + "798-1364" => "We're sorry, the call cannot be completed as dialed. + Please hang up and try again.", + "645-7689" => "Hello, this is Mr. Awesome's Pizza. My name is Fred. + What can I get for you today?", + _ => "Hi! Who is this again?" + } +} + +fn part0() { + let mut contacts = HashMap::new(); + + contacts.insert("Daniel", "798-1364"); + contacts.insert("Ashley", "645-7689"); + contacts.insert("Katie", "435-8291"); + contacts.insert("Robert", "956-1745"); + + // Takes a reference and returns Option<&V> + match contacts.get(&"Daniel") { + Some(&number) => println!("Calling Daniel: {}", call(number)), + _ => println!("Don't have Daniel's number."), + } + + // `HashMap::insert()` returns `None` + // if the inserted value is new, `Some(value)` otherwise + contacts.insert("Daniel", "164-6743"); + + match contacts.get(&"Ashley") { + Some(&number) => println!("Calling Ashley: {}", call(number)), + _ => println!("Don't have Ashley's number."), + } + + contacts.remove(&"Ashley"); + + // `HashMap::iter()` returns an iterator that yields + // (&'a key, &'a value) pairs in arbitrary order. + for (contact, &number) in contacts.iter() { + println!("Calling {}: {}", contact, call(number)); + } +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/std/hash/alt_key_types.rs b/src/bin/std/hash/alt_key_types.rs new file mode 100644 index 0000000..815c5e9 --- /dev/null +++ b/src/bin/std/hash/alt_key_types.rs @@ -0,0 +1,64 @@ +// ./src/std/hash/alt_key_types.md + + +use std::collections::HashMap; + +// Eq requires that you derive PartialEq on the type. +#[derive(PartialEq, Eq, Hash)] +struct Account<'a>{ + username: &'a str, + password: &'a str, +} + +struct AccountInfo<'a>{ + name: &'a str, + email: &'a str, +} + +type Accounts<'a> = HashMap, AccountInfo<'a>>; + +fn try_logon<'a>(accounts: &Accounts<'a>, + username: &'a str, password: &'a str){ + println!("Username: {}", username); + println!("Password: {}", password); + println!("Attempting logon..."); + + let logon = Account { + username, + password, + }; + + match accounts.get(&logon) { + Some(account_info) => { + println!("Successful logon!"); + println!("Name: {}", account_info.name); + println!("Email: {}", account_info.email); + }, + _ => println!("Login failed!"), + } +} + +fn part0(){ + let mut accounts: Accounts = HashMap::new(); + + let account = Account { + username: "j.everyman", + password: "password123", + }; + + let account_info = AccountInfo { + name: "John Everyman", + email: "j.everyman@email.com", + }; + + accounts.insert(account, account_info); + + try_logon(&accounts, "j.everyman", "psasword123"); + + try_logon(&accounts, "j.everyman", "password123"); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/std/hash/hashset.rs b/src/bin/std/hash/hashset.rs new file mode 100644 index 0000000..cc1b2c6 --- /dev/null +++ b/src/bin/std/hash/hashset.rs @@ -0,0 +1,43 @@ +// ./src/std/hash/hashset.md + + +use std::collections::HashSet; + +fn part0() { + let mut a: HashSet = vec![1i32, 2, 3].into_iter().collect(); + let mut b: HashSet = vec![2i32, 3, 4].into_iter().collect(); + + assert!(a.insert(4)); + assert!(a.contains(&4)); + + // `HashSet::insert()` returns false if + // there was a value already present. +// assert!(b.insert(4), "Value 4 is already in set B!"); + // FIXME ^ Comment out this line + + b.insert(5); + + // If a collection's element type implements `Debug`, + // then the collection implements `Debug`. + // It usually prints its elements in the format `[elem1, elem2, ...]` + println!("A: {:?}", a); + println!("B: {:?}", b); + + // Print [1, 2, 3, 4, 5] in arbitrary order + println!("Union: {:?}", a.union(&b).collect::>()); + + // This should print [1] + println!("Difference: {:?}", a.difference(&b).collect::>()); + + // Print [2, 3, 4] in arbitrary order. + println!("Intersection: {:?}", a.intersection(&b).collect::>()); + + // Print [1, 5] + println!("Symmetric Difference: {:?}", + a.symmetric_difference(&b).collect::>()); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/std/option.rs b/src/bin/std/option.rs new file mode 100644 index 0000000..9d8a8bf --- /dev/null +++ b/src/bin/std/option.rs @@ -0,0 +1,46 @@ +// ./src/std/option.md + + +// An integer division that doesn't `panic!` +fn checked_division(dividend: i32, divisor: i32) -> Option { + if divisor == 0 { + // Failure is represented as the `None` variant + None + } else { + // Result is wrapped in a `Some` variant + Some(dividend / divisor) + } +} + +// This function handles a division that may not succeed +fn try_division(dividend: i32, divisor: i32) { + // `Option` values can be pattern matched, just like other enums + match checked_division(dividend, divisor) { + None => println!("{} / {} failed!", dividend, divisor), + Some(quotient) => { + println!("{} / {} = {}", dividend, divisor, quotient) + }, + } +} + +fn part0() { + try_division(4, 2); + try_division(1, 0); + + // Binding `None` to a variable needs to be type annotated + let none: Option = None; + let _equivalent_none = None::; + + let optional_float = Some(0f32); + + // Unwrapping a `Some` variant will extract the value wrapped. + println!("{:?} unwraps to {:?}", optional_float, optional_float.unwrap()); + + // Unwrapping a `None` variant will `panic!` + println!("{:?} unwraps to {:?}", none, none.unwrap()); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/std/panic.rs b/src/bin/std/panic.rs new file mode 100644 index 0000000..3c12a76 --- /dev/null +++ b/src/bin/std/panic.rs @@ -0,0 +1,30 @@ +// ./src/std/panic.md + + +// Re-implementation of integer division (/) +fn division(dividend: i32, divisor: i32) -> i32 { + if divisor == 0 { + // Division by zero triggers a panic + panic!("division by zero"); + } else { + dividend / divisor + } +} + +// The `main` task +fn part0() { + // Heap allocated integer + let _x = Box::new(0i32); + + // This operation will trigger a task failure + division(3, 0); + + println!("This point won't be reached!"); + + // `_x` should get destroyed at this point +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/std/rc.rs b/src/bin/std/rc.rs new file mode 100644 index 0000000..872d7a3 --- /dev/null +++ b/src/bin/std/rc.rs @@ -0,0 +1,45 @@ +// ./src/std/rc.md + + +use std::rc::Rc; + +fn part0() { + let rc_examples = "Rc examples".to_string(); + { + println!("--- rc_a is created ---"); + + let rc_a: Rc = Rc::new(rc_examples); + println!("Reference Count of rc_a: {}", Rc::strong_count(&rc_a)); + + { + println!("--- rc_a is cloned to rc_b ---"); + + let rc_b: Rc = Rc::clone(&rc_a); + println!("Reference Count of rc_b: {}", Rc::strong_count(&rc_b)); + println!("Reference Count of rc_a: {}", Rc::strong_count(&rc_a)); + + // Two `Rc`s are equal if their inner values are equal + println!("rc_a and rc_b are equal: {}", rc_a.eq(&rc_b)); + + // We can use methods of a value directly + println!("Length of the value inside rc_a: {}", rc_a.len()); + println!("Value of rc_b: {}", rc_b); + + println!("--- rc_b is dropped out of scope ---"); + } + + println!("Reference Count of rc_a: {}", Rc::strong_count(&rc_a)); + + println!("--- rc_a is dropped out of scope ---"); + } + + // Error! `rc_examples` already moved into `rc_a` + // And when `rc_a` is dropped, `rc_examples` is dropped together + // println!("rc_examples: {}", rc_examples); + // TODO ^ Try uncommenting this line +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/std/result.rs b/src/bin/std/result.rs new file mode 100644 index 0000000..6678c71 --- /dev/null +++ b/src/bin/std/result.rs @@ -0,0 +1,66 @@ +// ./src/std/result.md + + +mod checked { + // Mathematical "errors" we want to catch + #[derive(Debug)] + pub enum MathError { + DivisionByZero, + NonPositiveLogarithm, + NegativeSquareRoot, + } + + pub type MathResult = Result; + + pub fn div(x: f64, y: f64) -> MathResult { + if y == 0.0 { + // This operation would `fail`, instead let's return the reason of + // the failure wrapped in `Err` + Err(MathError::DivisionByZero) + } else { + // This operation is valid, return the result wrapped in `Ok` + Ok(x / y) + } + } + + pub fn sqrt(x: f64) -> MathResult { + if x < 0.0 { + Err(MathError::NegativeSquareRoot) + } else { + Ok(x.sqrt()) + } + } + + pub fn ln(x: f64) -> MathResult { + if x <= 0.0 { + Err(MathError::NonPositiveLogarithm) + } else { + Ok(x.ln()) + } + } +} + +// `op(x, y)` === `sqrt(ln(x / y))` +fn op(x: f64, y: f64) -> f64 { + // This is a three level match pyramid! + match checked::div(x, y) { + Err(why) => panic!("{:?}", why), + Ok(ratio) => match checked::ln(ratio) { + Err(why) => panic!("{:?}", why), + Ok(ln) => match checked::sqrt(ln) { + Err(why) => panic!("{:?}", why), + Ok(sqrt) => sqrt, + }, + }, + } +} + +fn part0() { + // Will this fail? + println!("{}", op(1.0, 10.0)); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/std/result/question_mark.rs b/src/bin/std/result/question_mark.rs new file mode 100644 index 0000000..d82729d --- /dev/null +++ b/src/bin/std/result/question_mark.rs @@ -0,0 +1,71 @@ +// ./src/std/result/question_mark.md + + +mod checked { + #[derive(Debug)] + enum MathError { + DivisionByZero, + NonPositiveLogarithm, + NegativeSquareRoot, + } + + type MathResult = Result; + + fn div(x: f64, y: f64) -> MathResult { + if y == 0.0 { + Err(MathError::DivisionByZero) + } else { + Ok(x / y) + } + } + + fn sqrt(x: f64) -> MathResult { + if x < 0.0 { + Err(MathError::NegativeSquareRoot) + } else { + Ok(x.sqrt()) + } + } + + fn ln(x: f64) -> MathResult { + if x <= 0.0 { + Err(MathError::NonPositiveLogarithm) + } else { + Ok(x.ln()) + } + } + + // Intermediate function + fn op_(x: f64, y: f64) -> MathResult { + // if `div` "fails", then `DivisionByZero` will be `return`ed + let ratio = div(x, y)?; + + // if `ln` "fails", then `NonPositiveLogarithm` will be `return`ed + let ln = ln(ratio)?; + + sqrt(ln) + } + + pub fn op(x: f64, y: f64) { + match op_(x, y) { + Err(why) => panic!("{}", match why { + MathError::NonPositiveLogarithm + => "logarithm of non-positive number", + MathError::DivisionByZero + => "division by zero", + MathError::NegativeSquareRoot + => "square root of negative number", + }), + Ok(value) => println!("{}", value), + } + } +} + +fn part0() { + checked::op(1.0, 10.0); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/std/str.rs b/src/bin/std/str.rs new file mode 100644 index 0000000..1d05d27 --- /dev/null +++ b/src/bin/std/str.rs @@ -0,0 +1,69 @@ +// ./src/std/str.md + + +fn part0() { + // (all the type annotations are superfluous) + // A reference to a string allocated in read only memory + let pangram: &'static str = "the quick brown fox jumps over the lazy dog"; + println!("Pangram: {}", pangram); + + // Iterate over words in reverse, no new string is allocated + println!("Words in reverse"); + for word in pangram.split_whitespace().rev() { + println!("> {}", word); + } + + // Copy chars into a vector, sort and remove duplicates + let mut chars: Vec = pangram.chars().collect(); + chars.sort(); + chars.dedup(); + + // Create an empty and growable `String` + let mut string = String::new(); + for c in chars { + // Insert a char at the end of string + string.push(c); + // Insert a string at the end of string + string.push_str(", "); + } + + // The trimmed string is a slice to the original string, hence no new + // allocation is performed + let chars_to_trim: &[char] = &[' ', ',']; + let trimmed_str: &str = string.trim_matches(chars_to_trim); + println!("Used characters: {}", trimmed_str); + + // Heap allocate a string + let alice = String::from("I like dogs"); + // Allocate new memory and store the modified string there + let bob: String = alice.replace("dog", "cat"); + + println!("Alice says: {}", alice); + println!("Bob says: {}", bob); +} + +fn part1() { + // You can use escapes to write bytes by their hexadecimal values... + let byte_escape = "I'm writing \x52\x75\x73\x74!"; + println!("What are you doing\x3F (\\x3F means ?) {}", byte_escape); + + // ...or Unicode code points. + let unicode_codepoint = "\u{211D}"; + let character_name = "\"DOUBLE-STRUCK CAPITAL R\""; + + println!("Unicode character {} (U+211D) is called {}", + unicode_codepoint, character_name ); + + + let long_string = "String literals + can span multiple lines. + The linebreak and indentation here ->\ + <- can be escaped too!"; + println!("{}", long_string); +} + +pub fn main() { + part0(); + part1(); +} + diff --git a/src/bin/std/vec.rs b/src/bin/std/vec.rs new file mode 100644 index 0000000..485a4d6 --- /dev/null +++ b/src/bin/std/vec.rs @@ -0,0 +1,58 @@ +// ./src/std/vec.md + + +fn part0() { + // Iterators can be collected into vectors + let collected_iterator: Vec = (0..10).collect(); + println!("Collected (0..10) into: {:?}", collected_iterator); + + // The `vec!` macro can be used to initialize a vector + let mut xs = vec![1i32, 2, 3]; + println!("Initial vector: {:?}", xs); + + // Insert new element at the end of the vector + println!("Push 4 into the vector"); + xs.push(4); + println!("Vector: {:?}", xs); + + // Error! Immutable vectors can't grow +// collected_iterator.push(0); + // FIXME ^ Comment out this line + + // The `len` method yields the number of elements currently stored in a vector + println!("Vector length: {}", xs.len()); + + // Indexing is done using the square brackets (indexing starts at 0) + println!("Second element: {}", xs[1]); + + // `pop` removes the last element from the vector and returns it + println!("Pop last element: {:?}", xs.pop()); + + // Out of bounds indexing yields a panic +// println!("Fourth element: {}", xs[3]); + // FIXME ^ Comment out this line + + // `Vector`s can be easily iterated over + println!("Contents of xs:"); + for x in xs.iter() { + println!("> {}", x); + } + + // A `Vector` can also be iterated over while the iteration + // count is enumerated in a separate variable (`i`) + for (i, x) in xs.iter().enumerate() { + println!("In position {} we have value {}", i, x); + } + + // Thanks to `iter_mut`, mutable `Vector`s can also be iterated + // over in a way that allows modifying each value + for x in xs.iter_mut() { + *x *= 3; + } + println!("Updated vector: {:?}", xs); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/std_misc/arg.rs b/src/bin/std_misc/arg.rs new file mode 100644 index 0000000..3d1baf6 --- /dev/null +++ b/src/bin/std_misc/arg.rs @@ -0,0 +1,21 @@ +// ./src/std_misc/arg.md + + +use std::env; + +fn part0() { + let args: Vec = env::args().collect(); + + // The first argument is the path that was used to call the program. + println!("My path is {}.", args[0]); + + // The rest of the arguments are the passed command line parameters. + // Call the program like this: + // $ ./args arg1 arg2 + println!("I got {:?} arguments: {:?}.", args.len() - 1, &args[1..]); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/std_misc/arg/matching.rs b/src/bin/std_misc/arg/matching.rs new file mode 100644 index 0000000..3bdbdce --- /dev/null +++ b/src/bin/std_misc/arg/matching.rs @@ -0,0 +1,73 @@ +// ./src/std_misc/arg/matching.md + + +use std::env; + +fn increase(number: i32) { + println!("{}", number + 1); +} + +fn decrease(number: i32) { + println!("{}", number - 1); +} + +fn help() { + println!("usage: +match_args + Check whether given string is the answer. +match_args {{increase|decrease}} + Increase or decrease given integer by one."); +} + +fn part0() { + let args: Vec = env::args().collect(); + + match args.len() { + // no arguments passed + 1 => { + println!("My name is 'match_args'. Try passing some arguments!"); + }, + // one argument passed + 2 => { + match args[1].parse() { + Ok(42) => println!("This is the answer!"), + _ => println!("This is not the answer."), + } + }, + // one command and one argument passed + 3 => { + let cmd = &args[1]; + let num = &args[2]; + // parse the number + let number: i32 = match num.parse() { + Ok(n) => { + n + }, + Err(_) => { + eprintln!("error: second argument not an integer"); + help(); + return; + }, + }; + // parse the command + match &cmd[..] { + "increase" => increase(number), + "decrease" => decrease(number), + _ => { + eprintln!("error: invalid command"); + help(); + }, + } + }, + // all the other cases + _ => { + // show a help message + help(); + } + } +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/std_misc/channels.rs b/src/bin/std_misc/channels.rs new file mode 100644 index 0000000..3ed5d1c --- /dev/null +++ b/src/bin/std_misc/channels.rs @@ -0,0 +1,55 @@ +// ./src/std_misc/channels.md + + +use std::sync::mpsc::{Sender, Receiver}; +use std::sync::mpsc; +use std::thread; + +static NTHREADS: i32 = 3; + +fn part0() { + // Channels have two endpoints: the `Sender` and the `Receiver`, + // where `T` is the type of the message to be transferred + // (type annotation is superfluous) + let (tx, rx): (Sender, Receiver) = mpsc::channel(); + let mut children = Vec::new(); + + for id in 0..NTHREADS { + // The sender endpoint can be copied + let thread_tx = tx.clone(); + + // Each thread will send its id via the channel + let child = thread::spawn(move || { + // The thread takes ownership over `thread_tx` + // Each thread queues a message in the channel + thread_tx.send(id).unwrap(); + + // Sending is a non-blocking operation, the thread will continue + // immediately after sending its message + println!("thread {} finished", id); + }); + + children.push(child); + } + + // Here, all the messages are collected + let mut ids = Vec::with_capacity(NTHREADS as usize); + for _ in 0..NTHREADS { + // The `recv` method picks a message from the channel + // `recv` will block the current thread if there are no messages available + ids.push(rx.recv()); + } + + // Wait for the threads to complete any remaining work + for child in children { + child.join().expect("oops! the child thread panicked"); + } + + // Show the order in which the messages were sent + println!("{:?}", ids); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/std_misc/file/open.rs b/src/bin/std_misc/file/open.rs new file mode 100644 index 0000000..138ee90 --- /dev/null +++ b/src/bin/std_misc/file/open.rs @@ -0,0 +1,32 @@ +// ./src/std_misc/file/open.md + + +use std::fs::File; +use std::io::prelude::*; +use std::path::Path; + +fn part0() { + // Create a path to the desired file + let path = Path::new("hello.txt"); + let display = path.display(); + + // Open the path in read-only mode, returns `io::Result` + let mut file = match File::open(&path) { + Err(why) => panic!("couldn't open {}: {}", display, why), + Ok(file) => file, + }; + + // Read the file contents into a string, returns `io::Result` + let mut s = String::new(); + match file.read_to_string(&mut s) { + Err(why) => panic!("couldn't read {}: {}", display, why), + Ok(_) => print!("{} contains:\n{}", display, s), + } + + // `file` goes out of scope, and the "hello.txt" file gets closed +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/std_misc/path.rs b/src/bin/std_misc/path.rs new file mode 100644 index 0000000..b0e13a3 --- /dev/null +++ b/src/bin/std_misc/path.rs @@ -0,0 +1,35 @@ +// ./src/std_misc/path.md + + +use std::path::Path; + +fn part0() { + // Create a `Path` from an `&'static str` + let path = Path::new("."); + + // The `display` method returns a `Display`able structure + let _display = path.display(); + + // `join` merges a path with a byte container using the OS specific + // separator, and returns a `PathBuf` + let mut new_path = path.join("a").join("b"); + + // `push` extends the `PathBuf` with a `&Path` + new_path.push("c"); + new_path.push("myfile.tar.gz"); + + // `set_file_name` updates the file name of the `PathBuf` + new_path.set_file_name("package.tgz"); + + // Convert the `PathBuf` into a string slice + match new_path.to_str() { + None => panic!("new path is not a valid UTF-8 sequence"), + Some(s) => println!("new path is {}", s), + } +} + + +pub fn main() { + part0(); +} + diff --git a/src/bin/std_misc/process.rs b/src/bin/std_misc/process.rs new file mode 100644 index 0000000..891d1cc --- /dev/null +++ b/src/bin/std_misc/process.rs @@ -0,0 +1,27 @@ +// ./src/std_misc/process.md + + +use std::process::Command; + +fn part0() { + let output = Command::new("rustc") + .arg("--version") + .output().unwrap_or_else(|e| { + panic!("failed to execute process: {}", e) + }); + + if output.status.success() { + let s = String::from_utf8_lossy(&output.stdout); + + print!("rustc succeeded and stdout was:\n{}", s); + } else { + let s = String::from_utf8_lossy(&output.stderr); + + print!("rustc failed and stderr was:\n{}", s); + } +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/std_misc/threads.rs b/src/bin/std_misc/threads.rs new file mode 100644 index 0000000..7408821 --- /dev/null +++ b/src/bin/std_misc/threads.rs @@ -0,0 +1,29 @@ +// ./src/std_misc/threads.md + + +use std::thread; + +const NTHREADS: u32 = 10; + +// This is the `main` thread +fn part0() { + // Make a vector to hold the children which are spawned. + let mut children = vec![]; + + for i in 0..NTHREADS { + // Spin up another thread + children.push(thread::spawn(move || { + println!("this is thread number {}", i); + })); + } + + for child in children { + // Wait for the thread to finish. Returns a result. + let _ = child.join(); + } +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/std_misc/threads/testcase_mapreduce.rs b/src/bin/std_misc/threads/testcase_mapreduce.rs new file mode 100644 index 0000000..ee09a14 --- /dev/null +++ b/src/bin/std_misc/threads/testcase_mapreduce.rs @@ -0,0 +1,101 @@ +// ./src/std_misc/threads/testcase_mapreduce.md + + +use std::thread; + +// This is the `main` thread +fn part0() { + + // This is our data to process. + // We will calculate the sum of all digits via a threaded map-reduce algorithm. + // Each whitespace separated chunk will be handled in a different thread. + // + // TODO: see what happens to the output if you insert spaces! + let data = "86967897737416471853297327050364959 +11861322575564723963297542624962850 +70856234701860851907960690014725639 +38397966707106094172783238747669219 +52380795257888236525459303330302837 +58495327135744041048897885734297812 +69920216438980873548808413720956532 +16278424637452589860345374828574668"; + + // Make a vector to hold the child-threads which we will spawn. + let mut children = vec![]; + + /************************************************************************* + * "Map" phase + * + * Divide our data into segments, and apply initial processing + ************************************************************************/ + + // split our data into segments for individual calculation + // each chunk will be a reference (&str) into the actual data + let chunked_data = data.split_whitespace(); + + // Iterate over the data segments. + // .enumerate() adds the current loop index to whatever is iterated + // the resulting tuple "(index, element)" is then immediately + // "destructured" into two variables, "i" and "data_segment" with a + // "destructuring assignment" + for (i, data_segment) in chunked_data.enumerate() { + println!("data segment {} is \"{}\"", i, data_segment); + + // Process each data segment in a separate thread + // + // spawn() returns a handle to the new thread, + // which we MUST keep to access the returned value + // + // 'move || -> u32' is syntax for a closure that: + // * takes no arguments ('||') + // * takes ownership of its captured variables ('move') and + // * returns an unsigned 32-bit integer ('-> u32') + // + // Rust is smart enough to infer the '-> u32' from + // the closure itself so we could have left that out. + // + // TODO: try removing the 'move' and see what happens + children.push(thread::spawn(move || -> u32 { + // Calculate the intermediate sum of this segment: + let result = data_segment + // iterate over the characters of our segment.. + .chars() + // .. convert text-characters to their number value.. + .map(|c| c.to_digit(10).expect("should be a digit")) + // .. and sum the resulting iterator of numbers + .sum(); + + // println! locks stdout, so no text-interleaving occurs + println!("processed segment {}, result={}", i, result); + + // "return" not needed, because Rust is an "expression language", the + // last evaluated expression in each block is automatically its value. + result + + })); + } + + + /************************************************************************* + * "Reduce" phase + * + * Collect our intermediate results, and combine them into a final result + ************************************************************************/ + + // combine each thread's intermediate results into a single final sum. + // + // we use the "turbofish" ::<> to provide sum() with a type hint. + // + // TODO: try without the turbofish, by instead explicitly + // specifying the type of final_result + let final_result = children.into_iter().map(|c| c.join().unwrap()).sum::(); + + println!("Final sum result: {}", final_result); +} + + + +pub fn main() { + part0(); +} + diff --git a/src/bin/trait.rs b/src/bin/trait.rs new file mode 100644 index 0000000..d07f40f --- /dev/null +++ b/src/bin/trait.rs @@ -0,0 +1,76 @@ +// ./src/trait.md + + +struct Sheep { naked: bool, name: &'static str } + +trait Animal { + // Associated function signature; `Self` refers to the implementor type. + fn new(name: &'static str) -> Self; + + // Method signatures; these will return a string. + fn name(&self) -> &'static str; + fn noise(&self) -> &'static str; + + // Traits can provide default method definitions. + fn talk(&self) { + println!("{} says {}", self.name(), self.noise()); + } +} + +impl Sheep { + fn is_naked(&self) -> bool { + self.naked + } + + fn shear(&mut self) { + if self.is_naked() { + // Implementor methods can use the implementor's trait methods. + println!("{} is already naked...", self.name()); + } else { + println!("{} gets a haircut!", self.name); + + self.naked = true; + } + } +} + +// Implement the `Animal` trait for `Sheep`. +impl Animal for Sheep { + // `Self` is the implementor type: `Sheep`. + fn new(name: &'static str) -> Sheep { + Sheep { name: name, naked: false } + } + + fn name(&self) -> &'static str { + self.name + } + + fn noise(&self) -> &'static str { + if self.is_naked() { + "baaaaah?" + } else { + "baaaaah!" + } + } + + // Default trait methods can be overridden. + fn talk(&self) { + // For example, we can add some quiet contemplation. + println!("{} pauses briefly... {}", self.name, self.noise()); + } +} + +fn part0() { + // Type annotation is necessary in this case. + let mut dolly: Sheep = Animal::new("Dolly"); + // TODO ^ Try removing the type annotations. + + dolly.talk(); + dolly.shear(); + dolly.talk(); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/trait/clone.rs b/src/bin/trait/clone.rs new file mode 100644 index 0000000..47cb43d --- /dev/null +++ b/src/bin/trait/clone.rs @@ -0,0 +1,50 @@ +// ./src/trait/clone.md + + +// A unit struct without resources +#[derive(Debug, Clone, Copy)] +struct Unit; + +// A tuple struct with resources that implements the `Clone` trait +#[derive(Clone, Debug)] +struct Pair(Box, Box); + +fn part0() { + // Instantiate `Unit` + let unit = Unit; + // Copy `Unit`, there are no resources to move + let copied_unit = unit; + + // Both `Unit`s can be used independently + println!("original: {:?}", unit); + println!("copy: {:?}", copied_unit); + + // Instantiate `Pair` + let pair = Pair(Box::new(1), Box::new(2)); + println!("original: {:?}", pair); + + // Move `pair` into `moved_pair`, moves resources + let moved_pair = pair; + println!("moved: {:?}", moved_pair); + + // Error! `pair` has lost its resources + //println!("original: {:?}", pair); + // TODO ^ Try uncommenting this line + + // Clone `moved_pair` into `cloned_pair` (resources are included) + let cloned_pair = moved_pair.clone(); + // Drop the original pair using std::mem::drop + drop(moved_pair); + + // Error! `moved_pair` has been dropped + //println!("copy: {:?}", moved_pair); + // TODO ^ Try uncommenting this line + + // The result from .clone() can still be used! + println!("clone: {:?}", cloned_pair); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/trait/derive.rs b/src/bin/trait/derive.rs new file mode 100644 index 0000000..372fa54 --- /dev/null +++ b/src/bin/trait/derive.rs @@ -0,0 +1,53 @@ +// ./src/trait/derive.md + + +// `Centimeters`, a tuple struct that can be compared +#[derive(PartialEq, PartialOrd)] +struct Centimeters(f64); + +// `Inches`, a tuple struct that can be printed +#[derive(Debug)] +struct Inches(i32); + +impl Inches { + fn to_centimeters(&self) -> Centimeters { + let &Inches(inches) = self; + + Centimeters(inches as f64 * 2.54) + } +} + +// `Seconds`, a tuple struct with no additional attributes +struct Seconds(i32); + +fn part0() { + let _one_second = Seconds(1); + + // Error: `Seconds` can't be printed; it doesn't implement the `Debug` trait + //println!("One second looks like: {:?}", _one_second); + // TODO ^ Try uncommenting this line + + // Error: `Seconds` can't be compared; it doesn't implement the `PartialEq` trait + //let _this_is_true = (_one_second == _one_second); + // TODO ^ Try uncommenting this line + + let foot = Inches(12); + + println!("One foot equals {:?}", foot); + + let meter = Centimeters(100.0); + + let cmp = + if foot.to_centimeters() < meter { + "smaller" + } else { + "bigger" + }; + + println!("One foot is {} than one meter.", cmp); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/trait/disambiguating.rs b/src/bin/trait/disambiguating.rs new file mode 100644 index 0000000..bc81bdd --- /dev/null +++ b/src/bin/trait/disambiguating.rs @@ -0,0 +1,52 @@ +// ./src/trait/disambiguating.md + + +trait UsernameWidget { + // Get the selected username out of this widget + fn get(&self) -> String; +} + +trait AgeWidget { + // Get the selected age out of this widget + fn get(&self) -> u8; +} + +// A form with both a UsernameWidget and an AgeWidget +struct Form { + username: String, + age: u8, +} + +impl UsernameWidget for Form { + fn get(&self) -> String { + self.username.clone() + } +} + +impl AgeWidget for Form { + fn get(&self) -> u8 { + self.age + } +} + +fn part0() { + let form = Form { + username: "rustacean".to_owned(), + age: 28, + }; + + // If you uncomment this line, you'll get an error saying + // "multiple `get` found". Because, after all, there are multiple methods + // named `get`. + // println!("{}", form.get()); + + let username =
::get(&form); + assert_eq!("rustacean".to_owned(), username); + let age = ::get(&form); + assert_eq!(28, age); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/trait/drop.rs b/src/bin/trait/drop.rs new file mode 100644 index 0000000..7e541ea --- /dev/null +++ b/src/bin/trait/drop.rs @@ -0,0 +1,48 @@ +// ./src/trait/drop.md + + +struct Droppable { + name: &'static str, +} + +// This trivial implementation of `drop` adds a print to console. +impl Drop for Droppable { + fn drop(&mut self) { + println!("> Dropping {}", self.name); + } +} + +fn part0() { + let _a = Droppable { name: "a" }; + + // block A + { + let _b = Droppable { name: "b" }; + + // block B + { + let _c = Droppable { name: "c" }; + let _d = Droppable { name: "d" }; + + println!("Exiting block B"); + } + println!("Just exited block B"); + + println!("Exiting block A"); + } + println!("Just exited block A"); + + // Variable can be manually dropped using the `drop` function + drop(_a); + // TODO ^ Try commenting this line + + println!("end of the main function"); + + // `_a` *won't* be `drop`ed again here, because it already has been + // (manually) `drop`ed +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/trait/dyn.rs b/src/bin/trait/dyn.rs new file mode 100644 index 0000000..a1bc9cf --- /dev/null +++ b/src/bin/trait/dyn.rs @@ -0,0 +1,45 @@ +// ./src/trait/dyn.md + + +struct Sheep {} +struct Cow {} + +trait Animal { + // Instance method signature + fn noise(&self) -> &'static str; +} + +// Implement the `Animal` trait for `Sheep`. +impl Animal for Sheep { + fn noise(&self) -> &'static str { + "baaaaah!" + } +} + +// Implement the `Animal` trait for `Cow`. +impl Animal for Cow { + fn noise(&self) -> &'static str { + "moooooo!" + } +} + +// Returns some struct that implements Animal, but we don't know which one at compile time. +fn random_animal(random_number: f64) -> Box { + if random_number < 0.5 { + Box::new(Sheep {}) + } else { + Box::new(Cow {}) + } +} + +fn part0() { + let random_number = 0.234; + let animal = random_animal(random_number); + println!("You've randomly chosen an animal, and it says {}", animal.noise()); +} + + +pub fn main() { + part0(); +} + diff --git a/src/bin/trait/impl_trait.rs b/src/bin/trait/impl_trait.rs new file mode 100644 index 0000000..ea1aafd --- /dev/null +++ b/src/bin/trait/impl_trait.rs @@ -0,0 +1,94 @@ +// ./src/trait/impl_trait.md + + +fn parse_csv_document(src: R) -> std::io::Result>> { + src.lines() + .map(|line| { + // For each line in the source + line.map(|line| { + // If the line was read successfully, process it, if not, return the error + line.split(',') // Split the line separated by commas + .map(|entry| String::from(entry.trim())) // Remove leading and trailing whitespace + .collect() // Collect all strings in a row into a Vec + }) + }) + .collect() // Collect all lines into a Vec> +} + +fn parse_csv_document(src: impl std::io::BufRead) -> std::io::Result>> { + src.lines() + .map(|line| { + // For each line in the source + line.map(|line| { + // If the line was read successfully, process it, if not, return the error + line.split(',') // Split the line separated by commas + .map(|entry| String::from(entry.trim())) // Remove leading and trailing whitespace + .collect() // Collect all strings in a row into a Vec + }) + }) + .collect() // Collect all lines into a Vec> +} + +use std::iter; +use std::vec::IntoIter; + +// This function combines two `Vec` and returns an iterator over it. +// Look how complicated its return type is! +fn combine_vecs_explicit_return_type( + v: Vec, + u: Vec, +) -> iter::Cycle, IntoIter>> { + v.into_iter().chain(u.into_iter()).cycle() +} + +// This is the exact same function, but its return type uses `impl Trait`. +// Look how much simpler it is! +fn combine_vecs( + v: Vec, + u: Vec, +) -> impl Iterator { + v.into_iter().chain(u.into_iter()).cycle() +} + +fn part0() { + let v1 = vec![1, 2, 3]; + let v2 = vec![4, 5]; + let mut v3 = combine_vecs(v1, v2); + assert_eq!(Some(1), v3.next()); + assert_eq!(Some(2), v3.next()); + assert_eq!(Some(3), v3.next()); + assert_eq!(Some(4), v3.next()); + assert_eq!(Some(5), v3.next()); + println!("all done"); +} + +// Returns a function that adds `y` to its input +fn make_adder_function(y: i32) -> impl Fn(i32) -> i32 { + let closure = move |x: i32| { x + y }; + closure +} + +fn part1() { + let plus_one = make_adder_function(1); + assert_eq!(plus_one(2), 3); +} + +fn double_positives<'a>(numbers: &'a Vec) -> impl Iterator + 'a { + numbers + .iter() + .filter(|x| x > &&0) + .map(|x| x * 2) +} + +fn part2() { + let singles = vec![-3, -2, 2, 3]; + let doubles = double_positives(&singles); + assert_eq!(doubles.collect::>(), vec![4, 6]); +} + +pub fn main() { + part0(); + part1(); + part2(); +} + diff --git a/src/bin/trait/iter.rs b/src/bin/trait/iter.rs new file mode 100644 index 0000000..60e6ce7 --- /dev/null +++ b/src/bin/trait/iter.rs @@ -0,0 +1,79 @@ +// ./src/trait/iter.md + + +struct Fibonacci { + curr: u32, + next: u32, +} + +// Implement `Iterator` for `Fibonacci`. +// The `Iterator` trait only requires a method to be defined for the `next` element. +impl Iterator for Fibonacci { + // We can refer to this type using Self::Item + type Item = u32; + + // Here, we define the sequence using `.curr` and `.next`. + // The return type is `Option`: + // * When the `Iterator` is finished, `None` is returned. + // * Otherwise, the next value is wrapped in `Some` and returned. + // We use Self::Item in the return type, so we can change + // the type without having to update the function signatures. + fn next(&mut self) -> Option { + let current = self.curr; + + self.curr = self.next; + self.next = current + self.next; + + // Since there's no endpoint to a Fibonacci sequence, the `Iterator` + // will never return `None`, and `Some` is always returned. + Some(current) + } +} + +// Returns a Fibonacci sequence generator +fn fibonacci() -> Fibonacci { + Fibonacci { curr: 0, next: 1 } +} + +fn part0() { + // `0..3` is an `Iterator` that generates: 0, 1, and 2. + let mut sequence = 0..3; + + println!("Four consecutive `next` calls on 0..3"); + println!("> {:?}", sequence.next()); + println!("> {:?}", sequence.next()); + println!("> {:?}", sequence.next()); + println!("> {:?}", sequence.next()); + + // `for` works through an `Iterator` until it returns `None`. + // Each `Some` value is unwrapped and bound to a variable (here, `i`). + println!("Iterate through 0..3 using `for`"); + for i in 0..3 { + println!("> {}", i); + } + + // The `take(n)` method reduces an `Iterator` to its first `n` terms. + println!("The first four terms of the Fibonacci sequence are: "); + for i in fibonacci().take(4) { + println!("> {}", i); + } + + // The `skip(n)` method shortens an `Iterator` by dropping its first `n` terms. + println!("The next four terms of the Fibonacci sequence are: "); + for i in fibonacci().skip(4).take(4) { + println!("> {}", i); + } + + let array = [1u32, 3, 3, 7]; + + // The `iter` method produces an `Iterator` over an array/slice. + println!("Iterate the following array {:?}", &array); + for i in array.iter() { + println!("> {}", i); + } +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/trait/ops.rs b/src/bin/trait/ops.rs new file mode 100644 index 0000000..716c67c --- /dev/null +++ b/src/bin/trait/ops.rs @@ -0,0 +1,49 @@ +// ./src/trait/ops.md + + +use std::ops; + +struct Foo; +struct Bar; + +#[derive(Debug)] +struct FooBar; + +#[derive(Debug)] +struct BarFoo; + +// The `std::ops::Add` trait is used to specify the functionality of `+`. +// Here, we make `Add` - the trait for addition with a RHS of type `Bar`. +// The following block implements the operation: Foo + Bar = FooBar +impl ops::Add for Foo { + type Output = FooBar; + + fn add(self, _rhs: Bar) -> FooBar { + println!("> Foo.add(Bar) was called"); + + FooBar + } +} + +// By reversing the types, we end up implementing non-commutative addition. +// Here, we make `Add` - the trait for addition with a RHS of type `Foo`. +// This block implements the operation: Bar + Foo = BarFoo +impl ops::Add for Bar { + type Output = BarFoo; + + fn add(self, _rhs: Foo) -> BarFoo { + println!("> Bar.add(Foo) was called"); + + BarFoo + } +} + +fn part0() { + println!("Foo + Bar = {:?}", Foo + Bar); + println!("Bar + Foo = {:?}", Bar + Foo); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/trait/supertraits.rs b/src/bin/trait/supertraits.rs new file mode 100644 index 0000000..d64fcf8 --- /dev/null +++ b/src/bin/trait/supertraits.rs @@ -0,0 +1,39 @@ +// ./src/trait/supertraits.md + + +trait Person { + fn name(&self) -> String; +} + +// Person is a supertrait of Student. +// Implementing Student requires you to also impl Person. +trait Student: Person { + fn university(&self) -> String; +} + +trait Programmer { + fn fav_language(&self) -> String; +} + +// CompSciStudent (computer science student) is a subtrait of both Programmer +// and Student. Implementing CompSciStudent requires you to impl both supertraits. +trait CompSciStudent: Programmer + Student { + fn git_username(&self) -> String; +} + +fn comp_sci_student_greeting(student: &dyn CompSciStudent) -> String { + format!( + "My name is {} and I attend {}. My favorite language is {}. My Git username is {}", + student.name(), + student.university(), + student.fav_language(), + student.git_username() + ) +} + +fn part0() {} + +pub fn main() { + part0(); +} + diff --git a/src/bin/types/alias.rs b/src/bin/types/alias.rs new file mode 100644 index 0000000..aec2342 --- /dev/null +++ b/src/bin/types/alias.rs @@ -0,0 +1,25 @@ +// ./src/types/alias.md + + +// `NanoSecond`, `Inch`, and `U64` are new names for `u64`. +type NanoSecond = u64; +type Inch = u64; +type U64 = u64; + +fn part0() { + // `NanoSecond` = `Inch` = `U64` = `u64`. + let nanoseconds: NanoSecond = 5 as U64; + let inches: Inch = 2 as U64; + + // Note that type aliases *don't* provide any extra type safety, because + // aliases are *not* new types + println!("{} nanoseconds + {} inches = {} unit?", + nanoseconds, + inches, + nanoseconds + inches); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/types/cast.rs b/src/bin/types/cast.rs new file mode 100644 index 0000000..8b10b9d --- /dev/null +++ b/src/bin/types/cast.rs @@ -0,0 +1,86 @@ +// ./src/types/cast.md + + +// Suppress all warnings from casts which overflow. +#![allow(overflowing_literals)] + +fn part0() { + let decimal = 65.4321_f32; + + // Error! No implicit conversion +// let integer: u8 = decimal; + // FIXME ^ Comment out this line + + // Explicit conversion + let integer = decimal as u8; + let character = integer as char; + + // Error! There are limitations in conversion rules. + // A float cannot be directly converted to a char. +// let character = decimal as char; + // FIXME ^ Comment out this line + + println!("Casting: {} -> {} -> {}", decimal, integer, character); + + // when casting any value to an unsigned type, T, + // T::MAX + 1 is added or subtracted until the value + // fits into the new type + + // 1000 already fits in a u16 + println!("1000 as a u16 is: {}", 1000 as u16); + + // 1000 - 256 - 256 - 256 = 232 + // Under the hood, the first 8 least significant bits (LSB) are kept, + // while the rest towards the most significant bit (MSB) get truncated. + println!("1000 as a u8 is : {}", 1000 as u8); + // -1 + 256 = 255 + println!(" -1 as a u8 is : {}", (-1i8) as u8); + + // For positive numbers, this is the same as the modulus + println!("1000 mod 256 is : {}", 1000 % 256); + + // When casting to a signed type, the (bitwise) result is the same as + // first casting to the corresponding unsigned type. If the most significant + // bit of that value is 1, then the value is negative. + + // Unless it already fits, of course. + println!(" 128 as a i16 is: {}", 128 as i16); + + // 128 as i8 -> -128, whose two's complement in eight bits is: + println!(" 128 as a i8 is : {}", 128 as i8); + + // repeating the example above + // 1000 as u8 -> 232 + println!("1000 as a u8 is : {}", 1000 as u8); + // and the two's complement of 232 is -24 + println!(" 232 as a i8 is : {}", 232 as i8); + + // Since Rust 1.45, the `as` keyword performs a *saturating cast* + // when casting from float to int. If the floating point value exceeds + // the upper bound or is less than the lower bound, the returned value + // will be equal to the bound crossed. + + // 300.0 is 255 + println!("300.0 is {}", 300.0_f32 as u8); + // -100.0 as u8 is 0 + println!("-100.0 as u8 is {}", -100.0_f32 as u8); + // nan as u8 is 0 + println!("nan as u8 is {}", f32::NAN as u8); + + // This behavior incurs a small runtime cost and can be avoided + // with unsafe methods, however the results might overflow and + // return **unsound values**. Use these methods wisely: + unsafe { + // 300.0 is 44 + println!("300.0 is {}", 300.0_f32.to_int_unchecked::()); + // -100.0 as u8 is 156 + println!("-100.0 as u8 is {}", (-100.0_f32).to_int_unchecked::()); + // nan as u8 is 0 + println!("nan as u8 is {}", f32::NAN.to_int_unchecked::()); + } +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/types/inference.rs b/src/bin/types/inference.rs new file mode 100644 index 0000000..fdeaba8 --- /dev/null +++ b/src/bin/types/inference.rs @@ -0,0 +1,24 @@ +// ./src/types/inference.md + + +fn part0() { + // Because of the annotation, the compiler knows that `elem` has type u8. + let elem = 5u8; + + // Create an empty vector (a growable array). + let mut vec = Vec::new(); + // At this point the compiler doesn't know the exact type of `vec`, it + // just knows that it's a vector of something (`Vec<_>`). + + // Insert `elem` in the vector. + vec.push(elem); + // Aha! Now the compiler knows that `vec` is a vector of `u8`s (`Vec`) + // TODO ^ Try commenting out the `vec.push(elem)` line + + println!("{:?}", vec); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/types/literals.rs b/src/bin/types/literals.rs new file mode 100644 index 0000000..685910e --- /dev/null +++ b/src/bin/types/literals.rs @@ -0,0 +1,25 @@ +// ./src/types/literals.md + + +fn part0() { + // Suffixed literals, their types are known at initialization + let x = 1u8; + let y = 2u32; + let z = 3f32; + + // Unsuffixed literals, their types depend on how they are used + let i = 1; + let f = 1.0; + + // `size_of_val` returns the size of a variable in bytes + println!("size of `x` in bytes: {}", std::mem::size_of_val(&x)); + println!("size of `y` in bytes: {}", std::mem::size_of_val(&y)); + println!("size of `z` in bytes: {}", std::mem::size_of_val(&z)); + println!("size of `i` in bytes: {}", std::mem::size_of_val(&i)); + println!("size of `f` in bytes: {}", std::mem::size_of_val(&f)); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/unsafe.rs b/src/bin/unsafe.rs new file mode 100644 index 0000000..e89d538 --- /dev/null +++ b/src/bin/unsafe.rs @@ -0,0 +1,31 @@ +// ./src/unsafe.md + + +fn part0() { + let raw_p: *const u32 = &10; + + unsafe { + assert!(*raw_p == 10); + } +} + +use std::slice; + +fn part1() { + let some_vector = vec![1, 2, 3, 4]; + + let pointer = some_vector.as_ptr(); + let length = some_vector.len(); + + unsafe { + let my_slice: &[u32] = slice::from_raw_parts(pointer, length); + + assert_eq!(some_vector.as_slice(), my_slice); + } +} + +pub fn main() { + part0(); + part1(); +} + diff --git a/src/bin/variable_bindings.rs b/src/bin/variable_bindings.rs new file mode 100644 index 0000000..b71b6a7 --- /dev/null +++ b/src/bin/variable_bindings.rs @@ -0,0 +1,28 @@ +// ./src/variable_bindings.md + + +fn part0() { + 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 +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/variable_bindings/declare.rs b/src/bin/variable_bindings/declare.rs new file mode 100644 index 0000000..40af0ae --- /dev/null +++ b/src/bin/variable_bindings/declare.rs @@ -0,0 +1,31 @@ +// ./src/variable_bindings/declare.md + + +fn part0() { + // Declare a variable binding + let a_binding; + + { + let x = 2; + + // Initialize the binding + a_binding = x * x; + } + + println!("a binding: {}", a_binding); + + let another_binding; + + // Error! Use of uninitialized binding + println!("another binding: {}", another_binding); + // FIXME ^ Comment out this line + + another_binding = 1; + + println!("another binding: {}", another_binding); +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/variable_bindings/freeze.rs b/src/bin/variable_bindings/freeze.rs new file mode 100644 index 0000000..56d7b17 --- /dev/null +++ b/src/bin/variable_bindings/freeze.rs @@ -0,0 +1,25 @@ +// ./src/variable_bindings/freeze.md + + +fn part0() { + 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() { + part0(); +} + diff --git a/src/bin/variable_bindings/mut.rs b/src/bin/variable_bindings/mut.rs new file mode 100644 index 0000000..63897b6 --- /dev/null +++ b/src/bin/variable_bindings/mut.rs @@ -0,0 +1,23 @@ +// ./src/variable_bindings/mut.md + + +fn part0() { + 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 +} + +pub fn main() { + part0(); +} + diff --git a/src/bin/variable_bindings/scope.rs b/src/bin/variable_bindings/scope.rs new file mode 100644 index 0000000..5a41b6f --- /dev/null +++ b/src/bin/variable_bindings/scope.rs @@ -0,0 +1,46 @@ +// ./src/variable_bindings/scope.md + + +fn part0() { + // 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); +} + +fn part1() { + let shadowed_binding = 1; + + { + println!("before being shadowed: {}", shadowed_binding); + + // This binding *shadows* the outer one + let shadowed_binding = "abc"; + + println!("shadowed in inner block: {}", shadowed_binding); + } + println!("outside inner block: {}", shadowed_binding); + + // This binding *shadows* the previous binding + let shadowed_binding = 2; + println!("shadowed in outer block: {}", shadowed_binding); +} + +pub fn main() { + part0(); + part1(); +} +