diff --git a/contrib/rst_files_with_prelude.txt b/contrib/rst_files_with_prelude.txt index 519ec3958..7202cb72b 100644 --- a/contrib/rst_files_with_prelude.txt +++ b/contrib/rst_files_with_prelude.txt @@ -11,3 +11,4 @@ courses/gnat_project_facility/*.rst courses/gnatcoverage/*.rst courses/rust_essentials/*.rst courses/comprehensive_rust_training/*.rst +courses/misc_tools/*.rst diff --git a/courses/comprehensive_rust_training/030_types_and_values.rst b/courses/comprehensive_rust_training/030_types_and_values.rst index 4444b8c0b..1ed671742 100644 --- a/courses/comprehensive_rust_training/030_types_and_values.rst +++ b/courses/comprehensive_rust_training/030_types_and_values.rst @@ -41,4 +41,4 @@ Types And Values .. include:: 030_types_and_values/03_values.rst .. include:: 030_types_and_values/04_arithmetic.rst .. include:: 030_types_and_values/05_inference.rst -.. include:: 030_types_and_values/06_exercise.rst +.. include:: 030_types_and_values/99_lab.rst diff --git a/courses/comprehensive_rust_training/030_types_and_values/06_exercise.rst b/courses/comprehensive_rust_training/030_types_and_values/99_lab.rst similarity index 54% rename from courses/comprehensive_rust_training/030_types_and_values/06_exercise.rst rename to courses/comprehensive_rust_training/030_types_and_values/99_lab.rst index 6db2faa8a..0e62f558f 100644 --- a/courses/comprehensive_rust_training/030_types_and_values/06_exercise.rst +++ b/courses/comprehensive_rust_training/030_types_and_values/99_lab.rst @@ -3,7 +3,7 @@ Exercise: Fibonacci ===================== --------------------- -Exercise: Fibonacci +Fibonacci Problem --------------------- The Fibonacci sequence begins with :rust:`[0,1]`. For n>1, the n'th @@ -13,9 +13,9 @@ n-2'th Fibonacci numbers. Write a function :rust:`fib(n)` that calculates the n'th Fibonacci number. When will this function panic? -:: +.. code:: rust - {{#include exercise.rs:fib}} + fn fib(n: u32) -> u32 { if n < 2 { // The base case. return todo!("Implement this"); @@ -25,4 +25,26 @@ When will this function panic? } } - {{#include exercise.rs:main}} + fn main() { + let n = 20; + println!("fib({n}) = {}", fib(n)); + } + +--------------------- +Fibonacci Solution +--------------------- + +.. code:: rust + + fn fib(n: u32) -> u32 { + if n < 2 { + return n; + } else { + return fib(n - 1) + fib(n - 2); + } + } + + fn main() { + let n = 20; + println!("fib({n}) = {}", fib(n)); + } diff --git a/courses/comprehensive_rust_training/040_control_flow_basics.rst b/courses/comprehensive_rust_training/040_control_flow_basics.rst index 856f38bac..42c59ad0d 100644 --- a/courses/comprehensive_rust_training/040_control_flow_basics.rst +++ b/courses/comprehensive_rust_training/040_control_flow_basics.rst @@ -43,4 +43,4 @@ Control Flow Basics .. include:: 040_control_flow_basics/05_blocks_and_scopes.rst .. include:: 040_control_flow_basics/06_functions.rst .. include:: 040_control_flow_basics/07_macros.rst -.. include:: 040_control_flow_basics/08_exercise.rst +.. include:: 040_control_flow_basics/99_lab.rst diff --git a/courses/comprehensive_rust_training/040_control_flow_basics/08_exercise.rst b/courses/comprehensive_rust_training/040_control_flow_basics/99_lab.rst similarity index 53% rename from courses/comprehensive_rust_training/040_control_flow_basics/08_exercise.rst rename to courses/comprehensive_rust_training/040_control_flow_basics/99_lab.rst index 0284da288..72f5b05bf 100644 --- a/courses/comprehensive_rust_training/040_control_flow_basics/08_exercise.rst +++ b/courses/comprehensive_rust_training/040_control_flow_basics/99_lab.rst @@ -3,7 +3,7 @@ Exercise: Collatz Sequence ============================ ---------------------------- -Exercise: Collatz Sequence +Collatz Sequence Problem ---------------------------- The @@ -28,12 +28,37 @@ For example, beginning with ``n1`` = 3: Write a function to calculate the length of the collatz sequence for a given initial :rust:`n`. -:: +.. code:: rust - {{#include exercise.rs:collatz_length}} + /// Determine the length of the + /// collatz sequence beginning at `n`. + fn collatz_length(mut n: i32) -> u32 { todo!("Implement this") } - {{#include exercise.rs:tests}} + fn main() { + // should be 15 + println!("Length: {}", collatz_length(11)); + } + +---------------------------- +Collatz Sequence Solution +---------------------------- - {{#include exercise.rs:main}} +.. code:: rust + + /// Determine the length of the + /// collatz sequence beginning at `n`. + fn collatz_length(mut n: i32) -> u32 { + let mut len = 1; + while n > 1 { + n = if n % 2 == 0 { n / 2 } else { 3 * n + 1 }; + len += 1; + } + len + } + + fn main() { + // should be 15 + println!("Length: {}", collatz_length(11)); + } diff --git a/courses/comprehensive_rust_training/050_tuples_and_arrays.rst b/courses/comprehensive_rust_training/050_tuples_and_arrays.rst index 526591d33..2d7bbdc3d 100644 --- a/courses/comprehensive_rust_training/050_tuples_and_arrays.rst +++ b/courses/comprehensive_rust_training/050_tuples_and_arrays.rst @@ -40,4 +40,4 @@ Tuples And Arrays .. include:: 050_tuples_and_arrays/02_tuples.rst .. include:: 050_tuples_and_arrays/03_iteration.rst .. include:: 050_tuples_and_arrays/04_destructuring.rst -.. include:: 050_tuples_and_arrays/05_exercise.rst +.. include:: 050_tuples_and_arrays/99_lab.rst diff --git a/courses/comprehensive_rust_training/050_tuples_and_arrays/05_exercise.rst b/courses/comprehensive_rust_training/050_tuples_and_arrays/05_exercise.rst deleted file mode 100644 index 4208e9a4f..000000000 --- a/courses/comprehensive_rust_training/050_tuples_and_arrays/05_exercise.rst +++ /dev/null @@ -1,54 +0,0 @@ -========================= -Exercise: Nested Arrays -========================= - -------------------------- -Exercise: Nested Arrays -------------------------- - -Arrays can contain other arrays: - -.. code:: rust - - let array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; - -What is the type of this variable? - -Use an array such as the above to write a function :rust:`transpose` which -will transpose a matrix (turn rows into columns): - -Transpose - -.. math:: - - \begin{bmatrix} - 1 & 2 & 3 \\ - 4 & 5 & 6 \\ - 7 & 8 & 9 - \end{bmatrix} - -into - -.. math:: - - \begin{bmatrix} - 1 & 4 & 7 \\ - 2 & 5 & 8 \\ - 3 & 6 & 9 - \end{bmatrix} - -Copy the code below to https://play.rust-lang.org/ and implement the -function. This function only operates on 3x3 matrices. - -:: - - // TODO: remove this when you're done with your implementation. - #![allow(unused_variables, dead_code)] - - {{#include exercise.rs:transpose}} - unimplemented!() - } - - {{#include exercise.rs:tests}} - - {{#include exercise.rs:main}} diff --git a/courses/comprehensive_rust_training/050_tuples_and_arrays/99_lab.rst b/courses/comprehensive_rust_training/050_tuples_and_arrays/99_lab.rst new file mode 100644 index 000000000..76a127a83 --- /dev/null +++ b/courses/comprehensive_rust_training/050_tuples_and_arrays/99_lab.rst @@ -0,0 +1,87 @@ +========================= +Exercise: Nested Arrays +========================= + +------------------------- +Nested Arrays Problem +------------------------- + +Arrays can contain other arrays: + +.. code:: rust + + let array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; + +What is the type of this variable? + +Use an array such as the above to write a function :rust:`transpose` which +will transpose a matrix (turn rows into columns): + +Transpose + +.. math:: + + \begin{bmatrix} + 1 & 2 & 3 \\ + 4 & 5 & 6 \\ + 7 & 8 & 9 + \end{bmatrix} + +into + +.. math:: + + \begin{bmatrix} + 1 & 4 & 7 \\ + 2 & 5 & 8 \\ + 3 & 6 & 9 + \end{bmatrix} + +Copy the code below to https://play.rust-lang.org/ and implement the +function. This function only operates on 3x3 matrices. + +.. code:: rust + + fn transpose(matrix: [[i32; 3]; 3]) -> [[i32; 3]; 3] { + todo!() + } + + fn main() { + let matrix = [ + [101, 102, 103], // comment makes rustfmt add newline + [201, 202, 203], + [301, 302, 303], + ]; + + dbg!(matrix); + let transposed = transpose(matrix); + dbg!(transposed); + } + +------------------------- +Nested Arrays Solution +------------------------- + +.. code:: rust + + fn transpose(matrix: [[i32; 3]; 3]) -> [[i32; 3]; 3] { + let mut result = [[0; 3]; 3]; + for i in 0..3 { + for j in 0..3 { + result[j][i] = matrix[i][j]; + } + } + result + } + + fn main() { + let matrix = [ + [101, 102, 103], // comment makes rustfmt add newline + [201, 202, 203], + [301, 302, 303], + ]; + + dbg!(matrix); + let transposed = transpose(matrix); + dbg!(transposed); + } diff --git a/courses/comprehensive_rust_training/060_references.rst b/courses/comprehensive_rust_training/060_references.rst index cf81e3e22..adbaba6aa 100644 --- a/courses/comprehensive_rust_training/060_references.rst +++ b/courses/comprehensive_rust_training/060_references.rst @@ -41,4 +41,4 @@ References .. include:: 060_references/03_slices.rst .. include:: 060_references/04_strings.rst .. include:: 060_references/05_dangling.rst -.. include:: 060_references/06_exercise.rst +.. include:: 060_references/99_lab.rst diff --git a/courses/comprehensive_rust_training/060_references/06_exercise.rst b/courses/comprehensive_rust_training/060_references/06_exercise.rst deleted file mode 100644 index 2e5a800c5..000000000 --- a/courses/comprehensive_rust_training/060_references/06_exercise.rst +++ /dev/null @@ -1,34 +0,0 @@ -==================== -Exercise: Geometry -==================== - --------------------- -Exercise: Geometry --------------------- - -We will create a few utility functions for 3-dimensional geometry, -representing a point as :rust:`[f64;3]`. It is up to you to determine the -function signatures. - -:: - - // Calculate the magnitude of a vector by summing the squares of its coordinates - // and taking the square root. Use the `sqrt()` method to calculate the square - // root, like `v.sqrt()`. - - {{#include exercise.rs:magnitude}} - fn magnitude(...) -> f64 { - todo!() - } - - // Normalize a vector by calculating its magnitude and dividing all of its - // coordinates by that magnitude. - - {{#include exercise.rs:normalize}} - fn normalize(...) { - todo!() - } - - // Use the following `main` to test your work. - - {{#include exercise.rs:main}} diff --git a/courses/comprehensive_rust_training/060_references/99_lab.rst b/courses/comprehensive_rust_training/060_references/99_lab.rst new file mode 100644 index 000000000..d84747720 --- /dev/null +++ b/courses/comprehensive_rust_training/060_references/99_lab.rst @@ -0,0 +1,79 @@ +==================== +Exercise: Geometry +==================== + +-------------------- +Geometry Problem +-------------------- + +We will create a few utility functions for 3-dimensional geometry, +representing a point as :rust:`[f64;3]`. It is up to you to determine the +function signatures. + +:: + + // Calculate the magnitude of a vector by summing the squares of its coordinates + // and taking the square root. Use the `sqrt()` method to calculate the square + // root, like `v.sqrt()`. + + fn magnitude(...) -> f64 { + todo!() + } + + // Normalize a vector by calculating its magnitude and dividing all of its + // coordinates by that magnitude. + + fn normalize(...) { + todo!() + } + + // Use the following `main` to test your work. + + fn main() { + println!("Magnitude of a unit vector: {}", magnitude(&[0.0, 1.0, 0.0])); + + let mut v = [1.0, 2.0, 9.0]; + println!("Magnitude of {v:?}: {}", magnitude(&v)); + normalize(&mut v); + println!("Magnitude of {v:?} after normalization: {}", magnitude(&v)); + } + +-------------------- +Geometry Solution +-------------------- + +.. code:: rust + + /// Calculate the magnitude of the given vector. + fn magnitude(vector: &[f64; 3]) -> f64 { + let mut mag_squared = 0.0; + for coord in vector { + mag_squared += coord * coord; + } + mag_squared.sqrt() + } + + /// Change the magnitude of the vector to 1.0 without changing its direction. + fn normalize(vector: &mut [f64; 3]) { + let mag = magnitude(vector); + for item in vector { + *item /= mag; + } + } + + fn main() { + println!("Magnitude of a unit vector: {}", magnitude(&[0.0, 1.0, 0.0])); + + let mut v = [1.0, 2.0, 9.0]; + println!("Magnitude of {v:?}: {}", magnitude(&v)); + normalize(&mut v); + println!("Magnitude of {v:?} after normalization: {}", magnitude(&v)); + } + +------------------------ +Additional Information +------------------------ + +Note that in :rust:`normalize` we wrote :rust:`*item /= mag` to modify each element. + +This is because we’re iterating using a mutable reference to an array, which causes the :rust:`for` loop to give mutable references to each element. diff --git a/courses/comprehensive_rust_training/070_user_defined_types.rst b/courses/comprehensive_rust_training/070_user_defined_types.rst index 2823130b1..f791a320c 100644 --- a/courses/comprehensive_rust_training/070_user_defined_types.rst +++ b/courses/comprehensive_rust_training/070_user_defined_types.rst @@ -42,4 +42,4 @@ User Defined Types .. include:: 070_user_defined_types/04_aliases.rst .. include:: 070_user_defined_types/05_const.rst .. include:: 070_user_defined_types/06_static.rst -.. include:: 070_user_defined_types/07_exercise.rst +.. include:: 070_user_defined_types/99_lab.rst diff --git a/courses/comprehensive_rust_training/070_user_defined_types/07_exercise.rst b/courses/comprehensive_rust_training/070_user_defined_types/07_exercise.rst deleted file mode 100644 index 2dd9d48e3..000000000 --- a/courses/comprehensive_rust_training/070_user_defined_types/07_exercise.rst +++ /dev/null @@ -1,46 +0,0 @@ -=========================== -Exercise: Elevator Events -=========================== - ---------------------------- -Exercise: Elevator Events ---------------------------- - -We will create a data structure to represent an event in an elevator -control system. It is up to you to define the types and functions to -construct various events. Use :rust:`#[derive(Debug)]` to allow the types to -be formatted with :rust:`{:?}`. - -This exercise only requires creating and populating data structures so -that :rust:`main` runs without errors. The next part of the course will -cover getting data out of these structures. - -:: - - {{#include exercise.rs:event}} - // TODO: add required variants - } - - {{#include exercise.rs:direction}} - - {{#include exercise.rs:car_arrived}} - todo!() - } - - {{#include exercise.rs:car_door_opened}} - todo!() - } - - {{#include exercise.rs:car_door_closed}} - todo!() - } - - {{#include exercise.rs:lobby_call_button_pressed}} - todo!() - } - - {{#include exercise.rs:car_floor_button_pressed}} - todo!() - } - - {{#include exercise.rs:main}} diff --git a/courses/comprehensive_rust_training/070_user_defined_types/99_lab.rst b/courses/comprehensive_rust_training/070_user_defined_types/99_lab.rst new file mode 100644 index 000000000..94b6952fe --- /dev/null +++ b/courses/comprehensive_rust_training/070_user_defined_types/99_lab.rst @@ -0,0 +1,165 @@ +=========================== +Exercise: Elevator Events +=========================== + +--------------------------- +Elevator Events Problem +--------------------------- + +We will create a data structure to represent an event in an elevator +control system. It is up to you to define the types and functions to +construct various events. Use :rust:`#[derive(Debug)]` to allow the types to +be formatted with :rust:`{:?}`. + +This exercise only requires creating and populating data structures so +that :rust:`main` runs without errors. The next part of the course will +cover getting data out of these structures. + +.. code:: rust + + #[derive(Debug)] + /// An event in the elevator system that the controller must react to. + enum Event { + // TODO: add required variants + } + + /// A direction of travel. + #[derive(Debug)] + enum Direction { + Up, + Down, + } + + /// The car has arrived on the given floor. + fn car_arrived(floor: i32) -> Event { + todo!() + } + + /// The car doors have opened. + fn car_door_opened() -> Event { + todo!() + } + + /// The car doors have closed. + fn car_door_closed() -> Event { + todo!() + } + + /// A directional button was pressed in an elevator lobby on the given floor. + fn lobby_call_button_pressed(floor: i32, dir: Direction) -> Event { + todo!() + } + + /// A floor button was pressed in the elevator car. + fn car_floor_button_pressed(floor: i32) -> Event { + todo!() + } + +--------------------------- +Main Program +--------------------------- + +.. container:: latex_environment scriptsize + + .. code:: rust + + fn main() { + println!( + "A ground floor passenger has pressed the up button: {:?}", + lobby_call_button_pressed(0, Direction::Up) + ); + println!("The car has arrived on the ground floor: {:?}", car_arrived(0)); + println!("The car door opened: {:?}", car_door_opened()); + println!( + "A passenger has pressed the 3rd floor button: {:?}", + car_floor_button_pressed(3) + ); + println!("The car door closed: {:?}", car_door_closed()); + println!("The car has arrived on the 3rd floor: {:?}", car_arrived(3)); + } + +---------------------------------- +Elevator Events Solution - Types +---------------------------------- + +.. code:: rust + + #![allow(dead_code)] + + #[derive(Debug)] + /// An event in the elevator system that the controller must react to. + enum Event { + /// A button was pressed. + ButtonPressed(Button), + + /// The car has arrived at the given floor. + CarArrived(Floor), + + /// The car's doors have opened. + CarDoorOpened, + + /// The car's doors have closed. + CarDoorClosed, + } + + /// A floor is represented as an integer. + type Floor = i32; + + /// A direction of travel. + #[derive(Debug)] + enum Direction { + Up, + Down, + } + + /// A user-accessible button. + #[derive(Debug)] + enum Button { + /// A button in the elevator lobby on the given floor. + LobbyCall(Direction, Floor), + + /// A floor button within the car. + CarFloor(Floor), + } + +---------------------------------------- +Elevator Events Solution - Subprograms +---------------------------------------- + +.. code:: rust + + /// The car has arrived on the given floor. + fn car_arrived(floor: i32) -> Event { + Event::CarArrived(floor) + } + + /// The car doors have opened. + fn car_door_opened() -> Event { + Event::CarDoorOpened + } + + /// The car doors have closed. + fn car_door_closed() -> Event { + Event::CarDoorClosed + } + + /// A directional button was pressed in an elevator lobby on the given floor. + fn lobby_call_button_pressed(floor: i32, dir: Direction) -> Event { + Event::ButtonPressed(Button::LobbyCall(dir, floor)) + } + + /// A floor button was pressed in the elevator car. + fn car_floor_button_pressed(floor: i32) -> Event { + Event::ButtonPressed(Button::CarFloor(floor)) + } + +------------------------ +Additional Information +------------------------ + +.. code:: rust + + #![allow(dead_code)] + +* Only thing we ever do with :rust:`Event` type is print it +* Compiler thinks the code is unused, this directive prevents that diff --git a/courses/comprehensive_rust_training/080_pattern_matching.rst b/courses/comprehensive_rust_training/080_pattern_matching.rst index 6104446e0..d8b7086ff 100644 --- a/courses/comprehensive_rust_training/080_pattern_matching.rst +++ b/courses/comprehensive_rust_training/080_pattern_matching.rst @@ -40,4 +40,4 @@ Pattern Matching .. include:: 080_pattern_matching/02_destructuring_structs.rst .. include:: 080_pattern_matching/03_destructuring_enums.rst .. include:: 080_pattern_matching/04_let_control_flow.rst -.. include:: 080_pattern_matching/05_exercise.rst +.. include:: 080_pattern_matching/99_lab.rst diff --git a/courses/comprehensive_rust_training/080_pattern_matching/02_destructuring_structs.rst b/courses/comprehensive_rust_training/080_pattern_matching/02_destructuring_structs.rst index 7f6ba5a8a..142b942d8 100644 --- a/courses/comprehensive_rust_training/080_pattern_matching/02_destructuring_structs.rst +++ b/courses/comprehensive_rust_training/080_pattern_matching/02_destructuring_structs.rst @@ -8,9 +8,24 @@ Structs Like tuples, Struct can also be destructured by matching: -.. code:: rust +.. container:: latex_environment scriptsize - {{#include ../../third_party/rust-by-example/destructuring-structs.rs}} + .. code:: rust + + struct Foo { + x: (u32, u32), + y: u32, + } + + #[rustfmt::skip] + fn main() { + let foo = Foo { x: (1, 2), y: 3 }; + match foo { + Foo { x: (1, b), y } => println!("x.0 = 1, b = {b}, y = {y}"), + Foo { y: 2, x: i } => println!("y = 2, x = {i:?}"), + Foo { y, .. } => println!("y = {y}, other fields were ignored"), + } + } --------- Details @@ -23,3 +38,14 @@ Details hard to spot. Try changing the :rust:`2` in the second arm to a variable, and see that it subtly doesn't work. Change it to a :rust:`const` and see it working again. + +----------------- +More to Explore +----------------- + +* Try :rust:`match &foo` and check the type of captures. The pattern syntax remains the same, but the captures become shared references. This is :dfn:`match ergonomics` and is often useful with match self when implementing methods on an enum. + + * The same effect occurs with :rust:`match &mut foo` - the captures become exclusive references. + +* The distinction between a capture and a constant expression can be hard to spot. Try changing the **2** in the second arm to a variable, and see that it subtly doesn’t work. Change it to a const and see it working again. + diff --git a/courses/comprehensive_rust_training/080_pattern_matching/05_exercise.rst b/courses/comprehensive_rust_training/080_pattern_matching/05_exercise.rst deleted file mode 100644 index 308f5a17c..000000000 --- a/courses/comprehensive_rust_training/080_pattern_matching/05_exercise.rst +++ /dev/null @@ -1,57 +0,0 @@ -================================= -Exercise: Expression Evaluation -================================= - ---------------------------------- -Exercise: Expression Evaluation ---------------------------------- - -Let's write a simple recursive evaluator for arithmetic expressions. - -An example of a small arithmetic expression could be :rust:`10 + 20`, which -evaluates to :rust:`30`. We can represent the expression as a tree: - -.. image:: comprehensive_rust_training/pattern_matching_exercise_1.svg - :width: 40% - -A bigger and more complex expression would be -:rust:`(10 * 9) + ((3 - 4) * 5)`, which evaluate to :rust:`85`. We represent -this as a much bigger tree: - -.. image:: comprehensive_rust_training/pattern_matching_exercise_2.svg - -In code, we will represent the tree with two types: - -:: - - {{#include exercise.rs:Operation}} - - {{#include exercise.rs:Expression}} - -The :rust:`Box` type here is a smart pointer, and will be covered in detail -later in the course. An expression can be :dfn:`boxed` with :rust:`Box::new` as -seen in the tests. To evaluate a boxed expression, use the deref -operator (:rust:`*`) to "unbox" it: :rust:`eval(*boxed_expr)`. - -Copy and paste the code into the Rust playground, and begin implementing -:rust:`eval`. The final product should pass the tests. It may be helpful to -use :rust:`todo!()` and get the tests to pass one-by-one. You can also skip -a test temporarily with :rust:`#[ignore]`: - -.. code:: none - - #[test] - #[ignore] - fn test_value() { .. } - -:: - - {{#include exercise.rs:Operation}} - - {{#include exercise.rs:Expression}} - - {{#include exercise.rs:eval}} - todo!() - } - - {{#include exercise.rs:tests}} diff --git a/courses/comprehensive_rust_training/080_pattern_matching/99_lab.rst b/courses/comprehensive_rust_training/080_pattern_matching/99_lab.rst new file mode 100644 index 000000000..379402a69 --- /dev/null +++ b/courses/comprehensive_rust_training/080_pattern_matching/99_lab.rst @@ -0,0 +1,83 @@ +================================= +Exercise: Expression Evaluation +================================= + +---------------------------------- +Expression Evaluation Background +---------------------------------- + +Let's write a simple recursive evaluator for arithmetic expressions. + +An example of a small arithmetic expression could be :rust:`10 + 20`, which +evaluates to :rust:`30`. We can represent the expression as a tree: + +.. image:: comprehensive_rust_training/pattern_matching_exercise_1.svg + :width: 40% + +A bigger and more complex expression would be +:rust:`(10 * 9) + ((3 - 4) * 5)`, which evaluate to :rust:`85`. We represent +this as a much bigger tree: + +.. image:: comprehensive_rust_training/pattern_matching_exercise_2.svg + +--------------------------------- +Expression Evaluation Problem +--------------------------------- + +In code, we will represent the tree with two types: + +.. code:: rust + + /// An operation to perform on two subexpressions. + #[derive(Debug)] + enum Operation { + Add, + Sub, + Mul, + Div, + } + + /// An expression, in tree form. + #[derive(Debug)] + enum Expression { + /// An operation on two subexpressions. + Op { op: Operation, left: Box, right: Box }, + + /// A literal value + Value(i64), + } + +And then create an evaluator: + +.. code:: rust + + fn eval(e: Expression) -> i64 { + todo!() + } + +.. note:: The :rust:`Box` type here is a smart pointer, and will be covered in detail later in the course. To evaluate a boxed expression, use the deref operator (:rust:`*`) to "unbox" it: :rust:`eval(*boxed_expr)`. + +Copy and paste the code into the Rust playground, and begin implementing +:rust:`eval`. + +--------------------------------- +Expression Evaluation Problem +--------------------------------- + +.. code:: rust + + fn eval(e: Expression) -> i64 { + match e { + Expression::Op { op, left, right } => { + let left = eval(*left); + let right = eval(*right); + match op { + Operation::Add => left + right, + Operation::Sub => left - right, + Operation::Mul => left * right, + Operation::Div => left / right, + } + } + Expression::Value(v) => v, + } + } diff --git a/courses/comprehensive_rust_training/090_methods_and_traits.rst b/courses/comprehensive_rust_training/090_methods_and_traits.rst index d2ae4c12f..fd51d269c 100644 --- a/courses/comprehensive_rust_training/090_methods_and_traits.rst +++ b/courses/comprehensive_rust_training/090_methods_and_traits.rst @@ -39,4 +39,4 @@ Methods And Traits .. include:: 090_methods_and_traits/01_methods.rst .. include:: 090_methods_and_traits/02_traits.rst .. include:: 090_methods_and_traits/03_deriving.rst -.. include:: 090_methods_and_traits/04_exercise.rst +.. include:: 090_methods_and_traits/99_lab.rst diff --git a/courses/comprehensive_rust_training/090_methods_and_traits/04_exercise.rst b/courses/comprehensive_rust_training/090_methods_and_traits/04_exercise.rst deleted file mode 100644 index 41900af46..000000000 --- a/courses/comprehensive_rust_training/090_methods_and_traits/04_exercise.rst +++ /dev/null @@ -1,29 +0,0 @@ -======================== -Exercise: Logger Trait -======================== - ------------------------- -Exercise: Logger Trait ------------------------- - -Let's design a simple logging utility, using a trait :rust:`Logger` with a -:rust:`log` method. Code which might log its progress can then take an -:rust:`&impl Logger`. In testing, this might put messages in the test -logfile, while in a production build it would send messages to a log -server. - -However, the :rust:`StderrLogger` given below logs all messages, regardless -of verbosity. Your task is to write a :rust:`VerbosityFilter` type that will -ignore messages above a maximum verbosity. - -This is a common pattern: a struct wrapping a trait implementation and -implementing that same trait, adding behavior in the process. What other -kinds of wrappers might be useful in a logging utility? - -:: - - {{#include exercise.rs:setup}} - - // TODO: Define and implement `VerbosityFilter`. - - {{#include exercise.rs:main}} diff --git a/courses/comprehensive_rust_training/090_methods_and_traits/99_lab.rst b/courses/comprehensive_rust_training/090_methods_and_traits/99_lab.rst new file mode 100644 index 000000000..ab57202e8 --- /dev/null +++ b/courses/comprehensive_rust_training/090_methods_and_traits/99_lab.rst @@ -0,0 +1,64 @@ +======================== +Exercise: Logger Trait +======================== + +------------------------ +Logger Trait Problem +------------------------ + +Let's design a simple logging utility, using a trait :rust:`Logger` with a +:rust:`log` method. Code which might log its progress can then take an +:rust:`&impl Logger`. In testing, this might put messages in the test +logfile, while in a production build it would send messages to a log +server. + +However, the :rust:`StderrLogger` given below logs all messages, regardless +of verbosity. Your task is to write a :rust:`VerbosityFilter` type that will +ignore messages above a maximum verbosity. + +This is a common pattern: a struct wrapping a trait implementation and +implementing that same trait, adding behavior in the process. What other +kinds of wrappers might be useful in a logging utility? + +.. code:: rust + + trait Logger { + /// Log a message at the given verbosity level. + fn log(&self, verbosity: u8, message: &str); + } + + struct StderrLogger; + + impl Logger for StderrLogger { + fn log(&self, verbosity: u8, message: &str) { + eprintln!("verbosity={verbosity}: {message}"); + } + } + + /// Only log messages up to the given verbosity level. + struct VerbosityFilter { + max_verbosity: u8, + inner: StderrLogger, + } + + // TODO: Define and implement `VerbosityFilter`. + + fn main() { + let logger = VerbosityFilter { max_verbosity: 3, inner: StderrLogger }; + logger.log(5, "FYI"); + logger.log(2, "Uhoh"); + } + +------------------------ +Logger Trait Solution +------------------------ + +.. code:: rust + + impl Logger for VerbosityFilter { + fn log(&self, verbosity: u8, message: &str) { + if verbosity <= self.max_verbosity { + self.inner.log(verbosity, message); + } + } + } diff --git a/courses/comprehensive_rust_training/100_generics.rst b/courses/comprehensive_rust_training/100_generics.rst index a08b63e3f..58ffc932f 100644 --- a/courses/comprehensive_rust_training/100_generics.rst +++ b/courses/comprehensive_rust_training/100_generics.rst @@ -42,4 +42,4 @@ Generics .. include:: 100_generics/04_trait_bounds.rst .. include:: 100_generics/05_impl_trait.rst .. include:: 100_generics/06_dyn_trait.rst -.. include:: 100_generics/07_exercise.rst +.. include:: 100_generics/99_lab.rst diff --git a/courses/comprehensive_rust_training/100_generics/07_exercise.rst b/courses/comprehensive_rust_training/100_generics/07_exercise.rst deleted file mode 100644 index 8bdd5a346..000000000 --- a/courses/comprehensive_rust_training/100_generics/07_exercise.rst +++ /dev/null @@ -1,30 +0,0 @@ -=============================== -Exercise: Generic :rust:`min` -=============================== - -------------------------------- -Exercise: Generic :rust:`min` -------------------------------- - -In this short exercise, you will implement a generic :rust:`min` function -that determines the minimum of two values, using the -:url:`Ord ` -trait. - -:: - - use std::cmp::Ordering; - - // TODO: implement the `min` function used in `main`. - - {{#include exercise.rs:main}} - ---------- -Details ---------- - -- Show students the - :url:`Ord ` - trait and - :url:`Ordering ` - enum. diff --git a/courses/comprehensive_rust_training/100_generics/99_lab.rst b/courses/comprehensive_rust_training/100_generics/99_lab.rst new file mode 100644 index 000000000..c8eb5db17 --- /dev/null +++ b/courses/comprehensive_rust_training/100_generics/99_lab.rst @@ -0,0 +1,50 @@ +=============================== +Exercise: Generic :rust:`min` +=============================== + +------------------------------- +Generic :rust:`min` Program +------------------------------- + +In this short exercise, you will implement a generic :rust:`min` function +that determines the minimum of two values, using the +:url:`Ord ` +trait. + +.. code:: rust + + use std::cmp::Ordering; + + // TODO: implement the `min` function used in `main`. + + fn main() { + assert_eq!(min(0, 10), 0); + assert_eq!(min(500, 123), 123); + + assert_eq!(min('a', 'z'), 'a'); + assert_eq!(min('7', '1'), '1'); + + assert_eq!(min("hello", "goodbye"), "goodbye"); + assert_eq!(min("bat", "armadillo"), "armadillo"); + } + +------------------------------- +Generic :rust:`min` Solution +------------------------------- + +.. code:: rust + + fn min(l: T, r: T) -> T { + match l.cmp(&r) { + Ordering::Less | Ordering::Equal => l, + Ordering::Greater => r, + } + } + +------------------ +More Information +------------------ + +:url:`Ord trait ` + +:url:`Ordering enum ` diff --git a/courses/misc_tools/010_overview.rst b/courses/misc_tools/010_overview.rst new file mode 100644 index 000000000..cbe436411 --- /dev/null +++ b/courses/misc_tools/010_overview.rst @@ -0,0 +1,55 @@ +********** +Overview +********** + +.. container:: PRELUDE BEGIN + +.. container:: PRELUDE ROLES + +.. role:: ada(code) + :language: Ada + +.. role:: C(code) + :language: C + +.. role:: cpp(code) + :language: C++ + +.. role:: rust(code) + :language: Rust + +.. container:: PRELUDE SYMBOLS + +.. |rightarrow| replace:: :math:`\rightarrow` +.. |forall| replace:: :math:`\forall` +.. |exists| replace:: :math:`\exists` +.. |equivalent| replace:: :math:`\iff` +.. |le| replace:: :math:`\le` +.. |ge| replace:: :math:`\ge` +.. |lt| replace:: :math:`<` +.. |gt| replace:: :math:`>` +.. |checkmark| replace:: :math:`\checkmark` + +.. container:: PRELUDE REQUIRES + +.. container:: PRELUDE PROVIDES + +.. container:: PRELUDE END + +=================== +About This Course +=================== + +-------- +Styles +-------- + +* :dfn:`This` is a definition +* :filename:`this/is/a.path` +* :ada:`code is highlighted` +* :command:`commands are emphasised --like-this` + +.. warning:: This is a warning +.. note:: This is an important piece of info +.. tip:: This is a tip + diff --git a/courses/misc_tools/100_gnatstub.rst b/courses/misc_tools/100_gnatstub.rst new file mode 100644 index 000000000..037e225b3 --- /dev/null +++ b/courses/misc_tools/100_gnatstub.rst @@ -0,0 +1,262 @@ +********************** +:toolname:`GNATstub` +********************** + +.. container:: PRELUDE BEGIN + +.. container:: PRELUDE ROLES + +.. role:: ada(code) + :language: Ada + +.. role:: C(code) + :language: C + +.. role:: cpp(code) + :language: C++ + +.. role:: rust(code) + :language: Rust + +.. container:: PRELUDE SYMBOLS + +.. |rightarrow| replace:: :math:`\rightarrow` +.. |forall| replace:: :math:`\forall` +.. |exists| replace:: :math:`\exists` +.. |equivalent| replace:: :math:`\iff` +.. |le| replace:: :math:`\le` +.. |ge| replace:: :math:`\ge` +.. |lt| replace:: :math:`<` +.. |gt| replace:: :math:`>` +.. |checkmark| replace:: :math:`\checkmark` + +.. container:: PRELUDE REQUIRES + +.. container:: PRELUDE PROVIDES + +.. container:: PRELUDE END + +============== +Introduction +============== + +--------------------- +Body Stub Generator +--------------------- + +* Creates empty (but compilable) package/subprogram bodies +* Can use GNAT Project file + + * Configuration in package :ada:`gnatstub` + +* Default behavior is to raise exception if stub is called + + * It means you did not create a "real" body + +------------------------ +Why Do You Need Stubs? +------------------------ + +Sometimes we want to establish code structure quickly + + * Start prototyping code architecture first + * Worry about implementation details later + + * Don't want to get caught in compilation details/behavior in early development + +============================== +Running :toolname:`GNATstub` +============================== + +------------------------------ +Running :toolname:`GNATstub` +------------------------------ + +:command:`gnatstub [switches] {filename}` + +where :filename:`{filename}` can be a package spec or body + +* Package spec + + * :toolname:`GNATstub` will generate a package body containing "dummy" bodies for subprograms defined not completed in the spec + +* Package body + + * For any subprogram defined as :ada:`separate` in the package body, a file will be created containing a body for the subprogram + +.. note:: Need to specify :command:`--subunits` switch + +---------------------- +Example Package Spec +---------------------- + +* Filename :filename:`example.ads` contains + + .. code:: Ada + + package Example is + procedure Null_Procedure is null; + procedure Needs_A_Stub; + function Expression_Function return Integer is (1); + end Example; + +* :command:`gnatstub example.ads` will generate :filename:`example.adb` + + .. code:: Ada + + pragma Ada_2012; + package body Example is + + ------------------ + -- Needs_A_Stub -- + ------------------ + + procedure Needs_A_Stub is + begin + pragma Compile_Time_Warning + (Standard.True, "Needs_A_Stub unimplemented"); + raise Program_Error with "Unimplemented procedure Needs_A_Stub"; + end Needs_A_Stub; + + end Example; + +---------------------- +Example Package Body +---------------------- + +* Filename :filename:`example.adb` contains + + .. code:: Ada + + package body Example is + procedure Do_Something_Else; + procedure Do_Something is separate; + procedure Do_Something_Else is + begin + Do_Something; + end Do_Something_Else; + end Example; + +* :command:`gnatstub --subunits example.adb` will generate :filename:`example-do_something.adb` + + .. code:: Ada + + pragma Ada_2012; + separate (Example) + procedure Do_Something is + begin + pragma Compile_Time_Warning (Standard.True, "Do_Something unimplemented"); + raise Program_Error with "Unimplemented procedure Do_Something"; + end Do_Something; + +=============================== +:toolname:`GNATstub` Switches +=============================== + +---------------------------------- +Controlling Behavior When Called +---------------------------------- + +* By default, a stubbed subprogram will raise :ada:`Program_Error` when called + + * Procedures use a :ada:`raise` statement + * Functions use a :ada:`raise` expression in a :ada:`return` + + * To prevent warnings about no return in a function + +* You can disable the exception in procedures + + * Switch :command:`--no-exception` + +.. warning:: Functions still need a return statement, so :ada:`raise` expression is still present + +--------------------------- +Formatting Comment Blocks +--------------------------- + +* Sometimes you use :toolname:`GNATstub` to create a shell for your implementation + + * Having the tool populate the shell with comments can be helpful + +* Comment switches: + + :command:`--comment-header-sample` + + Create a file header comment block + + :command:`--comment-header-spec` + + Copy file header from spec into body + + :command:`--header-file=` + + Insert the contents of :filename:`` at the beginning of the stub body + +* Default behavior is to add a comment block for each subprogram + + * Use :command:`--no-local-header` to disable this + +----------------------- +Other Common Switches +----------------------- + +:command:`files=` + + :filename:`` contains a list of files for which stubs will be generated + +:command:`--force` + + Overwrite any existing file (without this, :toolname:`GNATstub` will flag as an error + +:command:`--output-dir=` + + Put generated files in :filename:`` + +:command:`max-line-length=` + + Maximum length of line in generated body. Default is 79, maximum is 32767 + +===== +Lab +===== + +.. include:: labs/100_gnatstub/lab.rst + +========= +Summary +========= + +----------------------------------- +Improving on :toolname:`GNATstub` +----------------------------------- + +* Sometimes empty code stubs aren't enough + + * Not only don't they do anything useful, they actively raise compiler warnings and run-time exceptions! + +* "Smart" stubs are useful for testing + + * Replace code not available for testing + * Control/replace external interfaces when testing natively + + * Read sensors + * Write to a console + +* You can modify the generated stub(s) do implement all this + +----------------------------- +Beyond :toolname:`GNATstub` +----------------------------- + +* User-created "Smart" stubs are great for testing + + * But there's a lot of repetition in building the stubs + * And maintenance can be difficult + +* Use :toolname:`GNATtest` to create more advanced unit tests + + * Expands on stubbing capabilities + * Adds test driver generation + * Adds automation capabilities + +For more information, go to :url:`GNATtest ` diff --git a/courses/misc_tools/README.md b/courses/misc_tools/README.md new file mode 100644 index 000000000..c0f0a7321 --- /dev/null +++ b/courses/misc_tools/README.md @@ -0,0 +1,12 @@ +# Overview + +This folder is a collection of modules for teaching various AdaCore/GNAT +tools. Each module is an RST file that focuses on one simple tool. +(More complicated tools should be in their own folder.) + +## Naming Scheme + +The module naming scheme uses a 3-digit prefix, followed by an underscore and +then the description of the module (all lower case, words separated by "\_"). +The file extension for all modules should be ".rst". In this folder, +the 3-digit prefix should be used for grouping purposes only. diff --git a/courses/misc_tools/course.toml b/courses/misc_tools/course.toml new file mode 100644 index 000000000..02ee42bfd --- /dev/null +++ b/courses/misc_tools/course.toml @@ -0,0 +1 @@ +name = "Miscellaneous Tools" diff --git a/courses/misc_tools/labs/100_gnatstub/answer/math.adb b/courses/misc_tools/labs/100_gnatstub/answer/math.adb new file mode 100644 index 000000000..01d711644 --- /dev/null +++ b/courses/misc_tools/labs/100_gnatstub/answer/math.adb @@ -0,0 +1,35 @@ +------------------------------------------------------------ +-- -- +-- MATH -- +-- -- +-- -- +-- Simplistic math package to add or subtract two numbers -- +-- -- +------------------------------------------------------------ + +pragma Ada_2012; +package body Math is + + --------------------- + -- Add_Two_Numbers -- + --------------------- + + procedure Add_Two_Numbers + (Result : out Integer; Param_A : Integer; Param_B : Integer) + is + begin + Result := Param_A + Param_B; + end Add_Two_Numbers; + + -------------------------- + -- Subtract_Two_Numbers -- + -------------------------- + + function Subtract_Two_Numbers + (Param_A : Integer; Param_B : Integer) return Integer + is + begin + return Param_A - Param_B; + end Subtract_Two_Numbers; + +end Math; diff --git a/courses/misc_tools/labs/100_gnatstub/lab.rst b/courses/misc_tools/labs/100_gnatstub/lab.rst new file mode 100644 index 000000000..979e9050c --- /dev/null +++ b/courses/misc_tools/labs/100_gnatstub/lab.rst @@ -0,0 +1,75 @@ +-------------------------- +:toolname:`GNATstub` Lab +-------------------------- + +* We are going implement a simple math package that does addition and subtraction + + * The exectuable takes 3 numbers on the command line - adds the first two, subtracts the third, and prints the result + +* Copy the :filename:`100_gnatstub` lab folder from the course materials location + +* Contents of the folder: + + * :filename:`default.gpr` - project file + * :filename:`main.adb` - main program + * :filename:`math.ads` - package spec that we want to implement + +.. note:: We use animation - if you don't know the answer, Page Down should give it to you + +---------------------- +Build the Executable +---------------------- + +1. Open a command prompt window and navigate to the directory containing :filename:`default.gpr` + +2. Try to build the exectuable (:command:`gprbuild -P default.gpr`) + + * Build fails because :ada:`Math` is not implemented + +3. Build a stub for :ada:`Math` + + * Make sure you copy the file header comment into the stub + +.. container:: animate 2- + + :command:`gnatstub --comment-header-spec math.ads` + +---------------------------- +Build the Executable Again +---------------------------- + +1. Build the executable again + + * Builds, but you get compile warnings from the stubbed subprograms + +2. Run the executable + + * Remember to add three numbers on the command line + +3. Executable should fail with :ada:`Program_Error` in :ada:`Add_Two_Numbers` + + * Default stub behavior + +4. Rebuild the stub without exceptions and run it again + +.. container:: animate 2- + + :command:`gnatstub -f --comment-header-spec --no-exception math.ads` + + * Exception now raised in :ada:`Subtract_Two_Numbers` + + * Exceptions always raised for functions in a stub + +----------------------- +Implement :ada:`Math` +----------------------- + +1. Edit the :ada:`Math` package body to implement the two subprograms + +2. Build and run the executable + +-------------------------- +:ada:`Math` Package Body +-------------------------- + +.. container:: source_include labs/100_gnatstub/answer/math.adb diff --git a/courses/misc_tools/labs/100_gnatstub/prompt/default.gpr b/courses/misc_tools/labs/100_gnatstub/prompt/default.gpr new file mode 100644 index 000000000..761e5d945 --- /dev/null +++ b/courses/misc_tools/labs/100_gnatstub/prompt/default.gpr @@ -0,0 +1,3 @@ +project Default is + for Main use ("main.adb"); +end Default; diff --git a/courses/misc_tools/labs/100_gnatstub/prompt/main.adb b/courses/misc_tools/labs/100_gnatstub/prompt/main.adb new file mode 100644 index 000000000..ffc7c7dfb --- /dev/null +++ b/courses/misc_tools/labs/100_gnatstub/prompt/main.adb @@ -0,0 +1,16 @@ +with Ada.Command_Line; use Ada.Command_Line; +with Ada.Text_IO; use Ada.Text_IO; +with Math; use Math; + +procedure Main is + One : Integer := Integer'Value (Ada.Command_Line.Argument (1)); + Two : Integer := Integer'Value (Ada.Command_Line.Argument (2)); + Three : Integer := Integer'Value (Ada.Command_Line.Argument (3)); + Result : Integer; +begin + Add_Two_Numbers (Result, One, Two); + Result := Subtract_Two_Numbers (Result, Three); + Put_Line + (One'Image & " + " & Two'Image & " - " & Three'Image & " = " & + Result'Image); +end Main; diff --git a/courses/misc_tools/labs/100_gnatstub/prompt/math.ads b/courses/misc_tools/labs/100_gnatstub/prompt/math.ads new file mode 100644 index 000000000..f9837a935 --- /dev/null +++ b/courses/misc_tools/labs/100_gnatstub/prompt/math.ads @@ -0,0 +1,21 @@ +------------------------------------------------------------ +-- -- +-- MATH -- +-- -- +-- -- +-- Simplistic math package to add or subtract two numbers -- +-- -- +------------------------------------------------------------ +package Math is + + procedure Add_Two_Numbers + (Result : out Integer; + Param_A : Integer; + Param_B : Integer); + + function Subtract_Two_Numbers + (Param_A : Integer; + Param_B : Integer) + return Integer; + +end Math;