diff --git a/contrib/rst_files_with_prelude.txt b/contrib/rst_files_with_prelude.txt index b085a298e..ac527f9ea 100644 --- a/contrib/rst_files_with_prelude.txt +++ b/contrib/rst_files_with_prelude.txt @@ -9,3 +9,4 @@ courses/static_analysis_via_compiler/*.rst courses/gnattest/*.rst courses/gnat_project_facility/*.rst courses/gnatcoverage/*.rst +courses/rust_essentials/*.rst diff --git a/courses/rust_essentials/010_rust_essentials.rst b/courses/rust_essentials/010_rust_essentials.rst new file mode 100644 index 000000000..ca9b6b73e --- /dev/null +++ b/courses/rust_essentials/010_rust_essentials.rst @@ -0,0 +1,52 @@ +***************** +Rust Essentials +***************** + +.. container:: PRELUDE BEGIN + +.. container:: PRELUDE ROLES + +.. role:: ada(code) + :language: Ada + +.. role:: C(code) + :language: C + +.. role:: cpp(code) + :language: C++ + +.. 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 + +.. include:: 010_rust_essentials/01-introduction_to_rust.rst +.. include:: 010_rust_essentials/02-procedural_language.rst +.. include:: 010_rust_essentials/03-language_quizzes.rst +.. include:: 010_rust_essentials/04-types.rst +.. include:: 010_rust_essentials/05-type_quizzes.rst +.. include:: 010_rust_essentials/06-functions_and_ownership.rst +.. include:: 010_rust_essentials/07-functions_and_ownership_quizzes.rst +.. include:: 010_rust_essentials/08-more_types.rst +.. include:: 010_rust_essentials/09-pattern_matching.rst +.. include:: 010_rust_essentials/10-traits_and_generics.rst +.. include:: 010_rust_essentials/11-traits_and_generics_quizzes.rst +.. include:: 010_rust_essentials/12-packages_and_modularity.rst +.. include:: 010_rust_essentials/13-functional_programming.rst +.. include:: 010_rust_essentials/14-functional_programming_quizzes.rst +.. include:: 010_rust_essentials/15-error_handling.rst +.. include:: 010_rust_essentials/16-smart_pointer_types.rst +.. include:: 010_rust_essentials/17-macros.rst diff --git a/courses/rust_essentials/010_rust_essentials/01-introduction_to_rust.rst b/courses/rust_essentials/010_rust_essentials/01-introduction_to_rust.rst new file mode 100644 index 000000000..cbb5351be --- /dev/null +++ b/courses/rust_essentials/010_rust_essentials/01-introduction_to_rust.rst @@ -0,0 +1,55 @@ +.. role:: rust(code) + :language: rust + +====================== +Introduction to Rust +====================== + +--------- +History +--------- + +* 2006 +* Personal project by Graydon Hoare (working @ Mozilla at the time) +* No specification, instead semantics are based on implementation +* Language changed *a lot* between 2006 and 2015 (and is still changing a lot + by other languages' standards) +* Nowadays, maintained and evolved by the Rust foundation + +------------------- +High Level Vision +------------------- + +* Safer alternative to C/C++ for systems programming +* Many inspirations, including ML family languages, C++ +* Focus on safety, albeit with a different perspective when compared to Ada + (memory safety being the most valued kind of safety) + +------------ +Rust Today +------------ + +* Use of Rust is spreading like wildfire +* Projects like Android, Linux +* Companies like Google, Amazon +* Well positioned to become a credible alternative to C++, and maybe even C +* Big list of industrial users here: https://www.rust-lang.org/production/users + +------------------------------- +In the Safety Critical Market +------------------------------- + +* Rust making forays into the critical markets. Big players are assessing the use of Rust in their codebases. +* But lacking industrial support for now +* Will probably become mainstream in the coming decade + +-------------------- +Rust "Hello World" +-------------------- + +.. code:: Rust + + fn main() { + println!("Hello, world!"); + } + diff --git a/courses/rust_essentials/010_rust_essentials/02-procedural_language.rst b/courses/rust_essentials/010_rust_essentials/02-procedural_language.rst new file mode 100644 index 000000000..936dbbc0e --- /dev/null +++ b/courses/rust_essentials/010_rust_essentials/02-procedural_language.rst @@ -0,0 +1,149 @@ +===================== +Procedural language +===================== + +-------------------------------- +First, a Note About Philosophy +-------------------------------- + +* In C/C++, very weak distinction between statements and expressions + + - You can use exprs as statements + +* In Ada, strong distinction between statements and expressions + + - Statements are statements, expressions are expressions, not interchangeable + - Procedures and functions are distinct + +* In Rust, everything is an expression (and you generally cannot ignore their value) + + - Simpler than Ada, (much) safer than C/C++ + - But not always obvious what an expression returns + - Complex type system tricks to make it work (what's the type of a loop?) + +----------- +For Loops +----------- + +.. code:: Rust + + fn main() { + for i in 1..10 { + // ^ Range object (of type Range) + println!("Hello, World!"); + } + } + +------------- +While Loops +------------- + +.. code:: Rust + + fn main() { + let mut i = 1; + // ^ Declare a mutable variable (immutable by default) + + // No parens around condition + while i < 10 { + println!("Hello, World!"); + i += 1; // increment + } + } + +---------------- +Infinite Loops +---------------- + +.. code:: Rust + + fn main() { + let mut i = 1; + + loop { + println!("Hello, World!"); + i += 1; // increment + + if i == 5 { + // ^ equality operator + break; + } + } + } + +--------------------------------- +Infinite Loop with Return Value +--------------------------------- + +.. code:: Rust + + fn main() { + let mut i = 1; + + let mut a = 0; + let mut b = 1; + + let res = loop { + let c = a + b; + a = b; + b = c; + i += 1; + if i > 12 { + break a; + } + }; + println!("{}", res); + } + +--------- +If/Else +--------- + +.. code:: Rust + + fn main() { + let mut i = 1; + loop { + if i == 5 || else i == 12 { + break; + } else if i < 5 && i > 2 { + println!("I = 3 or 4"); + } else { + println!("Hello, World!"); + } + } + } + +-------------------------- +If/Else As an Expression +-------------------------- + +.. code:: Rust + + fn main() { + let number = if true { 5 } else { 6 }; + + let error = if true { 5 } else { "six" }; + } + +------------------ +Match Expression +------------------ + +.. code:: Rust + + fn main() { + let mut i = 1; + + loop { + match i { + 5 | 12 => break, + 1..=4 => println!("i in 1..4"), + 7 | 9 => break, + _ => println!("Hello, World!") + } + + i += 1; + } + } + diff --git a/courses/rust_essentials/010_rust_essentials/03-language_quizzes.rst b/courses/rust_essentials/010_rust_essentials/03-language_quizzes.rst new file mode 100644 index 000000000..fa109631d --- /dev/null +++ b/courses/rust_essentials/010_rust_essentials/03-language_quizzes.rst @@ -0,0 +1,137 @@ +================== +Language Quizzes +================== + +---------------------------------------- +Quiz 1: Is There a Compilation Error? +---------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn main() { + let a = loop { + println!("Pouet"); + }; + + let b: u32 = a; + } + +.. container:: animate + + :color-green:`No error` + + *But you may get a warning that line 7 is unreachable* + +---------------------------------------- +Quiz 2: Is There a Compilation Error? +---------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn main() { + let a = for n in 1..11 { + println!("Pouet"); + }; + } + +.. container:: animate + + :color-green:`No error` + +---------------------------------------- +Quiz 3: Is There a Compilation Error? +---------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn main() { + let a = for n in 1..11 { + println!("Pouet"); + }; + + let b: u32 = a; + } + +.. container:: animate + + :color-red:`error[E0308]: mismatched types --> src/quiz.rs:7:21` + + Types of :rust:`a` and :rust:`b` are not the same + +---------------------------------------- +Quiz 4: Is There a Compilation Error? +---------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn main() { + let mut i = 1; + loop { + println!( + "{}", + if i == 5 || i == 12 { "5 or 12" } + else { "everything else" } + ); + + i += 1; + }; + } + +.. container:: animate + + :color-green:`No error` + +---------------------------------------- +Quiz 5: Is There a Compilation Error? +---------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn main() { + let mut i = 1; + + loop { + println!( + "{}", + if i == 5 || i == 12 { "5 or 12" } + else if i == 15 { "15" } + ); + + i += 1; + }; + } + +.. container:: animate + + :color-red:`error[E0317]: 'if' may be missing an 'else' clause --> src/quiz.rs:9:21` + + :rust:`if` expressions without :rust:`else` evaluate to :rust:`()` which is not a valid value for :rust:`println` + +---------------------------------------- +Quiz 6: Is There a Compilation Error? +---------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn main() { + let mut i = 100; + + while i { + i -= 1; + + println!("{}", i); + } + + } + +.. container:: animate + + :color-red:`error[E0308]: mismatched types --> src/quiz.rs:5:14` + + :rust:`while` condition expects a boolean value, but :rust:`i` is an integer diff --git a/courses/rust_essentials/010_rust_essentials/04-types.rst b/courses/rust_essentials/010_rust_essentials/04-types.rst new file mode 100644 index 000000000..3ca013dac --- /dev/null +++ b/courses/rust_essentials/010_rust_essentials/04-types.rst @@ -0,0 +1,154 @@ +======= +Types +======= + +--------------- +Numeric Types +--------------- + +* Set of built-in types: + + - Integer types: :rust:`i8`, :rust:`i16`, :rust:`i32`, :rust:`i64`, :rust:`i128` + - Unsigned types: :rust:`u8`, :rust:`u16`, :rust:`u32`, :rust:`u64`, :rust:`u128` + +* No way to define custom integer types +* Statically/strongly typed +* Two floating point types: :rust:`f32` and :rust:`f64` + +-------------------- +Other Scalar Types +-------------------- + +* Boolean: Named :rust:`bool`, either :rust:`true` or :rust:`false`. Not an enum! +* Character: Named :rust:`char`, can be any valid Unicode value. +* All in all, less powerful than Ada, but also much simpler. + +------------------- +Overflow Checking +------------------- + +* In debug builds: raises an error +* In release builds: wrap around +* Heritage of C++'s zero-cost abstraction mentality + +------------ +Tuple Type +------------ + +* Most basic composite type +* Anonymous collection of elements. +* Structurally typed + +.. code:: Rust + + fn main() { + let tp = (1, 2) + // ^ Type of this is (i32, i32) + + let (x, y) = tp; + // ^ This is an irrefutable pattern + + let f = tp.1; + // Access first value of tuple + } + +------------ +Array Type +------------ + +* Homogeneous array type +* Index type is usize +* Bounds checked +* Very simple (dare I say primitive). No variable length arrays at all. +* 90% of the time one will use vectors + +.. code:: Rust + + fn main() { + let a = [1, 2, 3, 4, 5]; + + println!("{}", a[4]); +} + +--------- +Vectors +--------- + +* As we said before, arrays in Rust are mostly useless +* In most cases you'll want to use vectors (:rust:`Vec`) +* Vectors can be variable size, and are growable, *but*, they're always heap + allocated + +.. code:: Rust + + fn main() { + let mut a = [1, 2, 3, 4].to_vec(); + // ^ Transform an array or slice into a vector + + let b = vec![1, 2, 3, 4]; + // Same thing as above + + let c = vec![1; 100]; + // Vector of 100 elements, all "1" + + println!("{:?}", a); + // ^ Print vector via the Debug trait + // If you can't print something, try this + + a.push(5); + println!("{:?}", a); + } + +-------- +Slices +-------- + +* Slices are a bit like arrays, but they just a view into a sequence. The type is written :rust:`[T]`, but is not used directly, but rather through pointers. + +.. code:: Rust + + fn main() { + let a = [1, 2, 3, 4, 5, 6, 7]; + let mut v = vec![1, 2, 3, 4, 5, 6, 7]; + + let b = &a[1 .. 3]; + // ^ Reference to a view of items 1 to 3 of the array a + + let c = &v[3 .. 5]; + // ^ Reference to a view of items 3 to 5 of the vec v + + println!("{:?}", c); + // By some ownership magic, after this statement, the lifetime of the + // reference c is over + + v.clear(); + + println!("{:?}", b); + } + +--------- +Strings +--------- + +There are two main string types in Rust + +* :rust:`String` is similar to a :rust:`Vec`, except: + + - It always points to a valid utf-8 sequence + - You cannot index it + +* :rust:`str` is a slice type. It is always used through a reference (:rust:`&str`) + +* An array of characters is *not* a :rust:`String` + +.. code:: Rust + + fn main() { + let message: &str = "Hello world"; + + for c in message.chars() { + print!("{}", c); + } + println!(""); + } + diff --git a/courses/rust_essentials/010_rust_essentials/05-type_quizzes.rst b/courses/rust_essentials/010_rust_essentials/05-type_quizzes.rst new file mode 100644 index 000000000..1daafad04 --- /dev/null +++ b/courses/rust_essentials/010_rust_essentials/05-type_quizzes.rst @@ -0,0 +1,144 @@ +============== +Type Quizzes +============== + +---------------------------------------- +Quiz 1: Is There a Compilation Error? +---------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn main() { + let i: (i32, i32) = [1, 2]; + } + +.. container:: animate + + :color-red:`error[E0308]: mismatched types --> src/quiz.rs:3:28` + + :rust:`[1, 2]` is an array but :rust:`i` is a defined as a tuple + +---------------------------------------- +Quiz 2: Is There a Compilation Error? +---------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn main() { + let i = [1, 2, 3, 4, 5.0]; + } + +.. container:: animate + + :color-red:`error[E0308]: mismatched types --> src/quiz.rs:3:29` + + :rust:`i` is an array, so all elements must be of the same type + +---------------------------------------- +Quiz 3: Is There a Compilation Error? +---------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn main() { + let i: [i32; 5] = [1, 2, 3, 4, 5]; + } + +.. container:: animate + + :color-green:`No error` + +---------------------------------------- +Quiz 4: Is There a Compilation Error? +---------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn main() { + let i: [i32] = [1, 2, 3, 4, 5]; + } + +.. container:: animate + + :color-red:`error[E0308]: mismatched types --> src/quiz.rs:3:23` + + :color-red:`error[E0277]: the size for values of type '[i32]' cannot be known at compilation time --> src/quiz.rs:3:12` + + TBD + +---------------------------------------- +Quiz 5: Is There a Compilation Error? +---------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn main() { + let n: i32 = 5; + let i: [i32; n] = [1, 2, 3, 4, 5]; + } + +.. container:: animate + + :color-red:`error[E0435]: attempt to use a non-constant value --> src/quiz.rs:4:21` + + Size of an array must be a constant :rust:`const` instead of a variable :rust:`let` + +---------------------------------------- +Quiz 6: Is There a Compilation Error? +---------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn main() { + let a = [1, 2, 3, 4, 5]; + + println!("{}", a[10]); + } + +.. container:: animate + + :color-red:`error: this operation will panic at runtime --> src/quiz.rs:5:23` + + Index (10) is out of bounds (array length is 5) + +---------------------------------------- +Quiz 7: Is There a Compilation Error? +---------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn main() { + let s: String = "Hai"; + println!("{}", s); + } + +.. container:: animate + + :color-red:`error[E0308]: mismatched types --> src/quiz.rs:3:24` + + :rust:`String` is similar to a vector; :rust:`s` should probably be :rust:`&str` + +---------------------------------------- +Quiz 8: Is There a Compilation Error? +---------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn main() { + let s: &str = "Hai"; + let s2: &str = &s[0..2]; + println!("{}", s); + } + +.. container:: animate + + :color-green:`No error` + diff --git a/courses/rust_essentials/010_rust_essentials/06-functions_and_ownership.rst b/courses/rust_essentials/010_rust_essentials/06-functions_and_ownership.rst new file mode 100644 index 000000000..2536b2b89 --- /dev/null +++ b/courses/rust_essentials/010_rust_essentials/06-functions_and_ownership.rst @@ -0,0 +1,115 @@ +========================= +Functions and Ownership +========================= + +----------- +Functions +----------- + +* Main is always called :rust:`main` +* You can put other functions at the top-level in your main source file +* Order doesn't matter + +.. code:: Rust + + fn main() { + println!("Pouet"); + other_function(); + } + + fn other_function() { + println("Pouet2"); + } + +--------------- +Functions (2) +--------------- + +* Functions contain a (possibly empty) sequence of statements, followed by an optional expression + +* Expression is used as the return value + +* An expression followed by a semicolon is a :dfn:`statement` + +.. code:: Rust + + fn fib() -> i32 { + let mut i = 1; + + let mut a = 0; + let mut b = 1; + + loop { + let c = a + b; + a = b; + b = c; + i += 1; + if i > 12 { + break a; + } + } + } + +----------- +Ownership +----------- + +.. code:: Rust + + fn double(v: Vec) -> Vec { + v.iter().map(|i| i * 2).collect() + // ^ Lambda function + // ^ Convert back to a vector + } + + fn main() { + let v: Vec = vec![1, 2, 3, 4]; + println!("{:?}", double(v)); + + println!("{:?}", v); // :( +} + +----------- +Ownership +----------- + +* Defining concept of Rust. Academic concept: Linear/Affine types +* By default, a value cannot be copied, only moved +* If you want to use it you either move it (as in the above example) or *borrow* it +* Two types of :dfn:`borrow`: Mutable (only one at a time), and immutable (N at a time) + +.. code:: Rust + + fn double(v: &Vec) -> Vec { + v.iter().map(|i| i * 2).collect() + } + + fn main() { + let v: Vec = vec![1, 2, 3, 4]; + println!("{:?}", double(&v)); + + println!("{:?}", v); // :( + } + +------------------------------- +Ownership: Mutable References +------------------------------- + +.. code:: Rust + + fn main() { + let mut v: Vec = vec![1, 2, 3, 4]; + let v2 = &mut v[1..3]; + v2[1] = 13; + println!("{:?}", v); + } + +-------------------------- +Ownership Is Complicated +-------------------------- + +* In many case you want to manipulate your data by reference but you can't use references + +* In those cases you want to use a managed pointer type: either :rust:`Box` (owned) or :rust:`Rc` (shared). + +* More details later diff --git a/courses/rust_essentials/010_rust_essentials/07-functions_and_ownership_quizzes.rst b/courses/rust_essentials/010_rust_essentials/07-functions_and_ownership_quizzes.rst new file mode 100644 index 000000000..7b649e661 --- /dev/null +++ b/courses/rust_essentials/010_rust_essentials/07-functions_and_ownership_quizzes.rst @@ -0,0 +1,138 @@ +================================= +Functions and Ownership Quizzes +================================= + +---------------------------------------- +Quiz 1: Is There a Compilation Error? +---------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn factorial(n: i64) -> i64 { + let mut ret = n; + + for i in 1..n { + ret = ret * n; + } + + ret; + } + +.. container:: animate + + :color-red:`error[E0308]: mismatched types --> src/quiz.rs:2:28` + + Function has no return *expression* - need to remove the ";" from line 9 + +---------------------------------------- +Quiz 2: Is There a Compilation Error? +---------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn double(v: &mut Vec) { + for i in 0..v.len() { + v[i] = v[i] * 2; + } + } + + fn main() { + let v: Vec = vec![1, 2, 3, 4]; + double(&v); + + println!("{:?}", v); // :( + } + +.. container:: animate + + :color-red:`error[E0308]: mismatched types --> src/quiz.rs:10:15` + + Actual parameter in call to :rust:`double` has different mutability than formal parameter + +---------------------------------------- +Quiz 3: Is There a Compilation Error? +---------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn double(v: &mut Vec) { + for i in 0..v.len() { + v[i] = v[i] * 2; + } + } + + fn main() { + let mut v: Vec = vec![1, 2, 3, 4]; + double(&v); + + println!("{:?}", v); // :( + } + +.. container:: animate + + :color-red:`error[E0308]: mismatched types --> src/quiz.rs:10:15` + + TBD + +---------------------------------------- +Quiz 4: Is There a Compilation Error? +---------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn double(v: &mut Vec) { + for i in 0..v.len() { + v[i] = v[i] * 2; + } + } + + fn main() { + let mut v: Vec = vec![1, 2, 3, 4]; + + let v2 = &mut v; + double(v2); + + let v3 = &mut v; + double(v3); + + println!("{:?}", v); // :( + } + +.. container:: animate + + :color-green:`No error` + +---------------------------------------- +Quiz 5: Is There a Compilation Error? +---------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn double(v: &mut Vec) { + for i in 0..v.len() { + v[i] = v[i] * 2; + } + } + + fn main() { + let mut v: Vec = vec![1, 2, 3, 4]; + + let v2 = &mut v; + double(v2); + + let v3 = &mut v; + double(v3); + + println!("{:?}", v2); // :( + } + +.. container:: animate + + :color-red:`error[E0499]: cannot borrow 'v' as mutable more than once at a time --> src/quiz.rs:14:17` + + :rust:`v2` already took ownership of :rust:`v` so :rust:`v3` cannot also take it diff --git a/courses/rust_essentials/010_rust_essentials/08-more_types.rst b/courses/rust_essentials/010_rust_essentials/08-more_types.rst new file mode 100644 index 000000000..8701eb4a1 --- /dev/null +++ b/courses/rust_essentials/010_rust_essentials/08-more_types.rst @@ -0,0 +1,141 @@ +============ +More Types +============ + +--------- +Structs +--------- + +.. code:: Rust + + #[derive(Debug)] + // Magic that allows you to print structs + struct Point { + x: i32, + // Component of the struct + y: i32 + } + + fn main() { + let p = Point { x: 12, y: 12 }; + println!("{:?}", p); + + println!("{}", p.x); + // ^ Access the field x + + // You can define mutable structs + let mut p2 = Point { x: 12, y: 12 }; + + // You can mutate fields of structs via dot notation + p2.x = 15; + + println!("{:?}", p2); + } + +------------------ +Structs: Methods +------------------ + +* Rust is not strictly an OOP language +* No inheritance +* No encapsulation +* BUT: You have method syntax :D + +------------------ +Structs: Methods +------------------ + +.. code:: Rust + + #[derive(Debug)] + struct Point { + x: i32, y: i32 + } + + impl Point { + fn invert(self: &Point) -> Point { + Point {x: self.y, y: self.x} + } + + fn double(&mut self) { + // ^ Alias for self: &mut Point + self.x = self.x * 2; + self.y = self.y * 2; + } + } + + fn main() { + let mut p = Point {x: 1, y: 2}; + p.double(); + + println!("{:?}", p); + println!("{:?}", p.invert()); + } + +------- +Enums +------- + +* Enums in Rust are very powerful +* Akin to sum types in functional languages +* But can also be used to model simple stuff +* Can also have methods, like structs! + +.. code:: Rust + + enum Color { + Yellow, Red, Green, Blue + } + + fn main() { + let y = Color::Yellow; + + match y { + Color::Yellow => println!("yellow!"), + Color::Red => println!("red!"); + _ => println!("Other color!"); + } + } + +--------------------- +Complex Enums (1/2) +--------------------- + +.. code:: Rust + + #[derive(Debug)] + enum Operator { + Plus, Minus, Divide, Multiply + } + + #[derive(Debug)] + enum Expr { + BinOp { + l: Box, + op: Operator, + r: Box + }, + Literal(i32) + } + +--------------------- +Complex Enums (2/2) +--------------------- + +.. code:: Rust + + fn main() { + let e = + Expr::BinOp { + l: Box::new( + Expr::BinOp { + l: Box::new(Expr::Literal(12)), + op: Operator::Plus, + r: Box::new(Expr::Literal(15)) + }), + op: Operator::Plus, + r: Box::new(Expr::Literal(12)) + }; + + println!("{:?}", e); + } diff --git a/courses/rust_essentials/010_rust_essentials/09-pattern_matching.rst b/courses/rust_essentials/010_rust_essentials/09-pattern_matching.rst new file mode 100644 index 000000000..1ec509fee --- /dev/null +++ b/courses/rust_essentials/010_rust_essentials/09-pattern_matching.rst @@ -0,0 +1,156 @@ +================== +Pattern Matching +================== + +------------------ +Pattern matching +------------------ + +.. code:: Rust + + match e { + // Matching on a struct inside an enum: + Expr::BinOp {l, op, r} => ... + + Expr::Literal(1..=5) => ... + // ^ Match an integer between 1 and 5 included + + Expr::Literal(8) => ... + // ^ Match an integer of value exactly 8 + + Expr::Literal(v) => ... + // Matching on a tuple inside an enum + + Expr::Literal(_) => ... + // Ignore the value + } + +------------------ +Pattern Matching +------------------ + +.. container:: latex_environment scriptsize + + .. code:: Rust + + struct Point { + x: i32, y: i32 + } + + // Pattern matching on a struct (kind of useless, gives warning used like that) + match p { + Point {x, y} => ... + } + + // Irrefutable pattern inside let + let Point {x, y} = p + + // Will only enter the body of the "if" if the match worked. + if let Expr::BinOp(l, op, r) = expr { + ... + } + +------------- +Option Type +------------- + +.. container:: latex_environment small + + .. code:: Rust + + use std::option::Option; + fn get_data() -> Vec { ... } + + fn main() { + let a = get_data(); + let val: Option = a.pop(); + // ^ Get last element of vec + + match val { + Some(val) => println!("Got value out of vector: {}", val), + None => println!("No value") + } + } + +* **Extremely** common type to represent possibility of a value +* Will be found everywhere + +------------- +Result Type +------------- + +* Generic type +* Error type (`E`) is often a string (`&'static str`, a static string reference) + +.. code:: Rust + + // Here is how the result type is defined in the stdlib + enum Result { + Ok(T), + Err(E), + } + +----------------- +Result Type (2) +----------------- + +.. code:: Rust + + fn main() { + let a = "10".parse::(); + + // Handle either option, error or OK + match a { + Ok(val) => println!("{val}") + Err(e) => println!("No value. Error: {e}") + } + + // Only handle OK + if let Some(val) = a { + println!("{val}") + } + + println!("{}", a.unwrap_or_else(|| 0)); + + // Panic on error + println!("{}", a.unwrap()); + } + +------------------------- +Pattern Matching: Loops +------------------------- + +.. code:: Rust + + fn get_data() -> Vec { ... } + + fn main() { + let data = get_data(); + + // Irrefutable pattern + for (idx, val) in data.iter().enumerate() { + } + + // Iterate while we can match the pattern + while let Some(a) = data.pop() { + ... + } + } + +--------------------------------------- +Pattern Matching: "let" and Functions +--------------------------------------- + +.. code:: Rust + + fn print_point_1(p: (i32, i32)) { + let (a, b) = p; + // ^ This is a pattern + println!("Current location: ({a}, {b})"); + } + + fn print_point_2((a, b): (i32, i32)) { + // ^ This is a pattern + println!("Current location: ({a}, {b})"); + } + diff --git a/courses/rust_essentials/010_rust_essentials/10-traits_and_generics.rst b/courses/rust_essentials/010_rust_essentials/10-traits_and_generics.rst new file mode 100644 index 000000000..5bc808822 --- /dev/null +++ b/courses/rust_essentials/010_rust_essentials/10-traits_and_generics.rst @@ -0,0 +1,218 @@ +===================== +Traits and Generics +===================== + +---------- +Generics +---------- + +.. code:: Rust + + struct LinkedList { + item: T, + next: Box> + } + +* Like Java/C# generics: abstract over types, functions, not packages +* Like Ada (& others): legality checked in the generic form +* Operations need to be made available on types (via traits) + +-------------- +Generics (2) +-------------- + +.. code:: Rust + + struct HashTable { ... } + + impl HashTable { + fn add(&self, item: T) { + // problem: how do we hash elements? + } + } + +-------- +Traits +-------- + +* Traits define common behavior +* Very similar to interfaces in Java/C#/etc +* But first and foremost a generic concept + +.. code:: Rust + + trait Hashable { + fn hash() -> i32; + } + + struct HashTable { } + // ^ Trait bound + + impl HashTable { + fn add(&self, item: T) { + ... + let hash = item.hash(); + ... + } + } + +----------------------------------------- +Shorthand for Trait Bounds in Functions +----------------------------------------- + +.. code:: Rust + + fn display_list(list: &[T]) { + for el in list { + print!("{el}"); + } + } + + // Shorthand: + + fn display_list(list: &[impl Display]) ... + // This function is a GENERIC function + +---------------------- +Some Built-In Traits +---------------------- + +* Rust has a lot of built-in traits that are part of the standard library +* Some of those are derivable: The compiler can provide an implementation for you automatically. + +* :rust:`Debug`: use to display a value using the :rust:`{:?}` formatter +* Ordering traits like :rust:`Eq`, :rust:`Ord` are used to compare values +* :rust:`Copy` and :rust:`Clone`, allow different copy semantics for your type. +* :rust:`Hash` computes a hash for your type + +To derive: + +.. code:: Rust + + #[derive(Hash, Debug)] + struct Point { + x: i32, y: i32 + } + // Point is now hashable and displayable via Debug trait + +---------------- +Copy and Clone +---------------- + +* The :rust:`Clone` trait adds a :rust:`clone` function on your type, that allows you to clone an instance of it. + +* The :rust:`Copy` trait, on the other hand, gives full copy semantics to your type (like you have by default on scalar types). + +.. code:: Rust + + #[derive(Copy, Debug)] + struct Point { + x: i32, y: i32 + } + + fn main() { + let p = Point { x = 1, y = 2 }; + let p2 = p; + + println!("{:?}", p); + // WHAT IS THIS SORCERY + } + +--------------------- +"Dyn" Trait Objects +--------------------- + +* You can store any object implementing a trait via the :rust:`dyn` qualifier, creating a trait object + +.. code:: Rust + + use std::fmt::Debug; + + fn main() { + let a: Vec> = vec![ + Box::new(12), + Box::new("pouet"), + Box::new((1, 2)) + ]; + println!("{:?}", a); + } + +----------- +Lifetimes +----------- + +Ownership is a combination of three things: + +* Basic rules of ownership (one owner, N borrowers, etc) +* Lifetimes for every value. For the moment, all lifetimes were infered. +* The borrow checker: checks that borrows don't outlive the lifetime of the value they borrow + +Turns out you can actually specify lifetimes yourself, allowing you to express +things that weren't possible before: + +.. code:: Rust + + // Won't work: can't return reference without explicit lifetime + fn smallest (a: &str, b: &str) -> &str { + if a < b { a } else { b } + } + + // Works + fn smallest <'a> (a: &'a str, b: &'a str) -> &'a str { + if a < b { a } else { b } + } + +--------------- +Lifetimes (2) +--------------- + +.. container:: latex_environment scriptsize + + .. code:: Rust + + fn smallest <'a> (a: &'a str, b: &'a str) -> &'a str { + if a < b { a } else { b } + } + + fn main() { + let a = String::from("abc"); // <-| Lifetime for a + let c; // | + { // | + let b = String::from("123"); // | <-| Lifetime for b (and hence for c) + c = smallest(&b, &a); // | | + println!("{}", c); // | <-| + } // | + println!("{}", c); // <-| + } + +--------------- +Lifetimes (3) +--------------- + +* Lifetimes are generic parameters, so functions using lifetimes are actually generic functions +* Structs using lifetimes are also generic types. If you want to use a reference in a struct, you need to annotate lifetimes + +.. code:: Rust + + struct Person<'a> { + first: &'a str, + last: &'a str + } + +------------------ +Lifetime Elision +------------------ + +.. code:: Rust + + // This works thanks to lifetime elision + fn identity(s: &str) -> &str { + s + } + +* Each parameter gets its own lifetime (input lifetimes) + +* If there is one input lifetime and one output lifetime, the output lifetime gets assigned to the input lifetime + +* If there are multiple params, but one of them is &self or &mut self, then the output lifetime gets assigned this lifetime + diff --git a/courses/rust_essentials/010_rust_essentials/11-traits_and_generics_quizzes.rst b/courses/rust_essentials/010_rust_essentials/11-traits_and_generics_quizzes.rst new file mode 100644 index 000000000..3f01fdc78 --- /dev/null +++ b/courses/rust_essentials/010_rust_essentials/11-traits_and_generics_quizzes.rst @@ -0,0 +1,113 @@ +============================= +Traits And Generics Quizzes +============================= + +--------------------------------------- +Quiz 1: Is There a Compilation Error? +--------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn largest(list: &[T]) -> &T { + let mut largest = &list[0]; + + for item in list { + if item > largest { + largest = item; + } + } + + largest + } + +.. container:: animate + + :color-red:`error[E0369]: binary operation '>' cannot be applied to type '&T' --> src/quiz.rs:6:20` + + TBD + +--------------------------------------- +Quiz 2: Is There a Compilation Error? +--------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn smallest <'a> (a: &'a str, b: &'a str) -> &'a str { + if a < b { a } else { b } + } + + fn main() { + let a = "hello"; + let c; + { + let b = "world"; + c = smallest(b, a); + println!("{}", c); + let d = b; + } + println!("{}", c); + } + +.. container:: animate + + :color-green:`No error` + +--------------------------------------- +Quiz 3: Is There a Compilation Error? +--------------------------------------- + +.. code:: Rust + :number-lines: 2 + + #[derive(Debug)] + struct Person<'a> { + first: &'a str, + last: &'a str + } + + fn main() { + let first = "Raphael".to_string(); + let p; + + { + let last = "Amiard".to_string(); + p = Person { first: &first, last: &last }; + } + println!("{:?}", p); + } + +.. container:: animate + + :color-red:`error[E0597]: `last` does not live long enough --> src/quiz.rs:14:46` + + TBD + +--------------------------------------- +Quiz 4: Is There a Compilation Error? +--------------------------------------- + +.. code:: Rust + :number-lines: 2 + + #[derive(Debug)] + struct Person<'a> { + first: &'a str, + last: &'a str + } + + fn main() { + let first = "Raphael".to_string(); + let p; + + { + let last = "Amiard".to_string(); + p = Person { first: &first, last: &last }; + println!("{:?}", p); + } + } + +.. container:: animate + + :color-green:`No error` diff --git a/courses/rust_essentials/010_rust_essentials/12-packages_and_modularity.rst b/courses/rust_essentials/010_rust_essentials/12-packages_and_modularity.rst new file mode 100644 index 000000000..59728e3c3 --- /dev/null +++ b/courses/rust_essentials/010_rust_essentials/12-packages_and_modularity.rst @@ -0,0 +1,96 @@ +========================= +Packages and Modularity +========================= + +---------------- +Modularity (1) +---------------- + +* Rust's compilation model is different from C/C++ +* Also very different from Ada +* Rust's compilation unit is the crate +* A crate can span several files, and is usually much bigger than an Ada or C compilation unit (C++ is different because of templates) + +Consequence is that parallel compilation is hampered in Rust. + +* Rust compiler is incremental on a sub-file level + +---------------- +Modularity (2) +---------------- + +* Two types of crates: Binary crates and library crates + + - Entry point for binary crates: main.rs + - Entry point for library crates: lib.rs + - Both can be redefined + +* Generally, a library = a crate (but a Cargo package can contain one or more crates) + +* A crate can be subdivided in modules + +--------- +Modules +--------- + +A crate can be further subdivided into modules + +* Modules provide scoping, organization, and encapsulation +* A module can be defined: + + - Inline + - In a file corresponding to the module name + +* By default, a module is private +* By default, items in a module are private + +.. code:: Rust + + // Inline module + pub mod ExprEval { + pub struct Expr { + } + + ... + } + +--------- +Modules +--------- + +.. code:: Rust + + // Module in a separate file + + // main.rs + + pub mod ExprEval + + // expreval.rs + + pub struct Expr { + } + +--------- +Modules +--------- + +.. code:: Rust + + // Module in a separate file, in a nested dir + + // main.rs + + pub mod ExprEval + + // expreval.rs + + pub mod Eval; + + pub struct Expr { + } + + // expreval/eval.rs + + pub fn eval(...) + diff --git a/courses/rust_essentials/010_rust_essentials/13-functional_programming.rst b/courses/rust_essentials/010_rust_essentials/13-functional_programming.rst new file mode 100644 index 000000000..d266e806b --- /dev/null +++ b/courses/rust_essentials/010_rust_essentials/13-functional_programming.rst @@ -0,0 +1,79 @@ +======================== +Functional Programming +======================== + +---------------------------------- +Functional Programming: Closures +---------------------------------- + +* In Rust, functions and closures are different +* Closures can be nested in functions, and can capture functions from their environment, which regular functions cannot + +.. code:: Rust + + fn main() { + let y = 12; + let adder = |x| x + y; + println!("{}", adder(12)); + } + +---------------------------------- +Functional Programming: Closures +---------------------------------- + +* External variables are captured via borrow, so regular borrow rules apply! +* You can explicitly move captured values + +.. code:: Rust + + use std::thread; + + fn main() { + let list = vec![1, 2, 3]; + println!("Before defining closure: {:?}", list); + + thread::spawn(move || println!("From thread {:?}", list)) + .join() + .unwrap(); + } + +------------------------------------------------ +Functional Programming: Closures and iterators +------------------------------------------------ + +.. code:: Rust + + fn main() { + let v = vec![1, 2, 3, 4, 5]; + + let sum = v.iter() + .map(|el| el * el) + .reduce(|acc, el| acc + el); + + println!("{}", sum.unwrap()); + + v.iter().for_each(|el| { + println!("{}", el); + }) + } + +* Rust has *many* methods like this on iterators + +------------------------------------------------ +Functional Programming: Closures and Iterators +------------------------------------------------ + +.. code:: Rust + + fn main() { + let v = HashMap::from([ + ("one", 1), + ("two", 2) + ]); + + let v2: HashMap = + v.iter().map(|(x, y)| (*y, *x)).collect(); + + println!("{:?}", v2); + } + diff --git a/courses/rust_essentials/010_rust_essentials/14-functional_programming_quizzes.rst b/courses/rust_essentials/010_rust_essentials/14-functional_programming_quizzes.rst new file mode 100644 index 000000000..6c6b0c539 --- /dev/null +++ b/courses/rust_essentials/010_rust_essentials/14-functional_programming_quizzes.rst @@ -0,0 +1,104 @@ +================================ +Functional Programming Quizzes +================================ + +-------------------------------------- +Quiz 1: Is This a Compilation Error? +-------------------------------------- + +.. code:: Rust + :number-lines: 2 + + fn main() { + let mut y = 12; + let adder = |x| x + y; + y = 15; + println!("{}", adder(12)); + } + +.. container:: animate + + :color-red:`error[E0506]: cannot assign to 'y' because it is borrowed --> src/quiz.rs:5:8` + + TBD + +-------------------------------------- +Quiz 2: Is This a Compilation Error? +-------------------------------------- + +.. code:: Rust + :number-lines: 2 + + use std::cell::RefCell; + + fn main() { + let y = RefCell::new(12); + let adder = |x| x + *y.borrow(); + *y.borrow_mut() = 15; + println!("{}", adder(12)); + } + +.. container:: animate + + :color-green:`No error` + +-------------------------------------- +Quiz 3: Is This a Compilation Error? +-------------------------------------- + +.. code:: Rust + :number-lines: 2 + + use std::cell::RefCell; + + struct Adder { + adder_fn: Box i32> + } + + fn create_adder(val: RefCell) -> Adder { + Adder {adder_fn: Box::new(|x| x + *val.borrow())} + } + + fn main() { + let v = RefCell::new(12); + let adder = create_adder(v); + println!("{}", *v.borrow()); + } + +.. container:: animate + + :color-red:`error[E0597]: 'val' does not live long enough --> src/quiz.rs:9:43` + + :color-red:`error[E0382]: borrow of moved value: 'v' --> src/quiz.rs:15:24` + + TBD + +-------------------------------------- +Quiz 4: Is This a Compilation Error? +-------------------------------------- + +.. code:: Rust + :number-lines: 2 + + use std::cell::RefCell; + use std::rc::Rc; + + struct Adder { + adder_fn: Box i32> + } + + fn create_adder(val: Rc>) -> Adder { + Adder {adder_fn: Box::new(move |x| x + *val.borrow())} + } + + fn main() { + let v = Rc::new(RefCell::new(12)); + let adder = create_adder(v.clone()); + println!("{}", (adder.adder_fn)(12)); + *v.borrow_mut() = 15; + println!("{}", (adder.adder_fn)(12)); + } + +.. container:: animate + + :color-green:`No error` diff --git a/courses/rust_essentials/010_rust_essentials/15-error_handling.rst b/courses/rust_essentials/010_rust_essentials/15-error_handling.rst new file mode 100644 index 000000000..41c772072 --- /dev/null +++ b/courses/rust_essentials/010_rust_essentials/15-error_handling.rst @@ -0,0 +1,99 @@ +================ +Error Handling +================ + +------- +PANIC +------- + +* Rust has no exceptions +* The closest thing it has is unrecoverable errors (via :rust:`panic!`) +* Obviously not a solution for robust applications + +.. code:: Rust + + fn main() { + let v = vec![1, 2, 3]; + + v[99]; // PANIC + } + +------------ +Backtraces +------------ + +When your program panics, running it with :command:`RUST_BACKTRACE=1` will show you a backtrace: + +.. container:: latex_environment tiny + + :: + + $ RUST_BACKTRACE=1 cargo run + thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', src/main.rs:4:5 + stack backtrace: + 0: rust_begin_unwind + at /rustc/3b348d932aa5c9884310d025cf7c516023fd0d9a/library/std/src/panicking.rs:584:5 + 1: core::panicking::panic_fmt + at /rustc/3b348d932aa5c9884310d025cf7c516023fd0d9a/library/core/src/panicking.rs:143:14 + 2: core::panicking::panic_bounds_check + at /rustc/3b348d932aa5c9884310d025cf7c516023fd0d9a/library/core/src/panicking.rs:85:5 + 3: >::index + at /rustc/3b348d932aa5c9884310d025cf7c516023fd0d9a/library/core/src/slice/index.rs:189:10 + 4: core::slice::index:: for [T]>::index + at /rustc/3b348d932aa5c9884310d025cf7c516023fd0d9a/library/core/src/slice/index.rs:15:9 + 5: as core::ops::index::Index>::index + at /rustc/3b348d932aa5c9884310d025cf7c516023fd0d9a/library/alloc/src/vec/mod.rs:2531:9 + 6: test_epita::main + at ./src/main.rs:4:5 + 7: core::ops::function::FnOnce::call_once + at /rustc/3b348d932aa5c9884310d025cf7c516023fd0d9a/library/core/src/ops/function.rs:227:5 + +-------- +Result +-------- + +* Proper way to handle errors is via the :rust:`Result` type (shown earlier). +* TIP: Main can return a :rust:`Result` (but only with () as an OK type): +* Rust provides the :rust:`?` operator for easy(er) error handling + +.. container:: latex_environment footnotesize + + .. code:: Rust + + use std::num::ParseIntError; + + fn main() -> Result<(), ParseIntError> { + let num_str = "10a"; + + let n = num_str.parse::()?; + // ^ Either unwrap, or return error result + println!("{}", n); + Ok(()) + } + +------------ +Result (2) +------------ + +You can also use early return for easier error handling + +.. code:: Rust + + use std::num::ParseIntError; + + fn main() -> Result<(), ParseIntError> { + let numbers = ["12", "15", "18a"]; + let mut n = 0; + + for num in numbers { + match num.parse::() { + Ok(val) => { n += val; } + Err(e) => { + return Err(e); + } + } + }; + + Ok(()) + } + diff --git a/courses/rust_essentials/010_rust_essentials/16-smart_pointer_types.rst b/courses/rust_essentials/010_rust_essentials/16-smart_pointer_types.rst new file mode 100644 index 000000000..b8a8a04a5 --- /dev/null +++ b/courses/rust_essentials/010_rust_essentials/16-smart_pointer_types.rst @@ -0,0 +1,59 @@ +===================== +Smart Pointer Types +===================== + +----- +Box +----- + +Box is a simple reference. Used when you want to *store* a reference, rather than just *borrow* it (see the expression evaluator exercise). + +.. code:: Rust + + fn main() { + let b = Box::new(5); + println!("b = {}", b); + } + +--------- +Box (2) +--------- + +* You cannot have multiple references to a box!! + +.. code:: Rust + + enum List { + Cons(i32, Box), + Nil, + } + + use crate::List::{Cons, Nil}; + + fn main() { + let a = Cons(5, Box::new(Cons(10, Box::new(Nil)))); + let b = Cons(3, Box::new(a)); + let c = Cons(4, Box::new(a)); + } + +---- +Rc +---- + +.. container:: latex_environment small + + .. code:: Rust + + enum List { + Cons(i32, Rc), + Nil, + } + + use crate::List::{Cons, Nil}; + use std::rc::Rc; + + fn main() { + let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil))))); + let b = Cons(3, Rc::clone(&a)); + let c = Cons(4, Rc::clone(&a)); + } diff --git a/courses/rust_essentials/010_rust_essentials/17-macros.rst b/courses/rust_essentials/010_rust_essentials/17-macros.rst new file mode 100644 index 000000000..ff241a502 --- /dev/null +++ b/courses/rust_essentials/010_rust_essentials/17-macros.rst @@ -0,0 +1,175 @@ +======== +Macros +======== + +--------------- +Macro Example +--------------- + +.. code:: Rust + + #[macro_export] + macro_rules! vec { + ( $( $x:expr ),* ) => { + { + let mut temp_vec = Vec::new(); + $( + temp_vec.push($x); + )* + temp_vec + } + }; + } + +-------- +Macros +-------- + +* Macros allow you to extend your language's syntax and semantics, by extending what it's able to do at compile time. + +* Bad macros work on text (like C-like macros) + +* Good macros work on structured input + + - In LISP, it worked on lists + - In Rust, it works on ASTs, token trees, or token streams. + +-------------------- +When to Use Macros +-------------------- + +* Never +* Never + +---------------------- +But Seriously Though +---------------------- + +* Macros are used to: + + - Abstract common repetitive programming patterns + - Embed domain specific languages + - Provide lazy evaluation + +* Generally: Macros are a last resort. Anything that you can solve another way shouldn't be fixed with macros. + +--------------------------- +The Rust Parsing Pipeline +--------------------------- + +* First, tokenization: Turn string into tokens. + +* From there, Rust can either: + + 1. Produce a syntax tree from tokens + 2. Produce a token tree that your macro can match upon + +-------------------- +Declarative Macros +-------------------- + +* Like the :rust:`vec` one. +* You use a form of pattern-matching on the arguments +* Used to provide function-like macros + +------------------------ +Declarative Macros (2) +------------------------ + +.. container:: latex_environment tiny + + .. code:: Rust + + macro_rules! ok_or_return { + ($e:expr, $err:expr) => { + { + match $e { + Ok(value) => value, + Err(_) => return Err($err) + } + } + } + } + + fn main() -> Result<(), &'static str> { + let mut line = String::new(); + ok_or_return!(std::io::stdin().read_line(&mut line), "Cannot read line"); // including '\n' + let a = ok_or_return!(line.trim().parse::(), "Cannot parse string"); + Ok(()) + } + +----------------------------------------- +Declarative Macros - Variadic Arguments +----------------------------------------- + +.. code:: Rust + + macro_rules! vec_strs { + ( + // Start a repetition: + $( + $element:expr // Each repeat must contain an expression... + ) + , // ...separated by commas... + * // ...zero or more times. + ) => { + // Enclose the expansion in a block so that we can use + // multiple statements. + { + let mut v = Vec::new(); + // Start a repetition: + $( + // Each repeat will contain the following statement, with + // $element replaced with the corresponding expression. + v.push(format!("{}", $element)); + )* + v + } + }; + } + +--------- +Hygiene +--------- + +.. code:: C + + #define INCI(i) do { int a=0; ++i; } while (0) + int main(void) + { + int a = 4, b = 8; + INCI(a); + INCI(b); + printf("a is now %d, b is now %d\n", a, b); + return 0; + } + +------------------ +Hygiene and Rust +------------------ + +.. code:: Rust + + macro_rules! using_a { + ($e:expr) => { + { + let a = 42; + $e + } + } + } + + let four = using_a!(a / 10); // Won't work + +------------------- +Procedural Macros +------------------- + +.. code:: Rust + + use proc_macro::TokenStream; + + #[proc_macro] + pub fn tlborm_fn_macro(input: TokenStream) -> TokenStream { + input + } diff --git a/courses/rust_essentials/README.md b/courses/rust_essentials/README.md new file mode 100644 index 000000000..6b290b40a --- /dev/null +++ b/courses/rust_essentials/README.md @@ -0,0 +1,16 @@ +# Overview + +This folder is a collection of modules for teaching the Rust language. + +The file **standard_course.txt** contains a list of all the modules that +we expect to find in a typical *Rust Essentials* course. If you need +to modify the list of included modules, this is the file you would change. + +This course was copied from https://github.com/raph-amiard/LASY-epita + +## 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". The 3-digit prefix +should be used to suggest the proper order for teaching the modules. diff --git a/courses/rust_essentials/course.toml b/courses/rust_essentials/course.toml new file mode 100644 index 000000000..05d8a16a8 --- /dev/null +++ b/courses/rust_essentials/course.toml @@ -0,0 +1 @@ +name = "Rust Essentials" diff --git a/courses/rust_essentials/standard_course.txt b/courses/rust_essentials/standard_course.txt new file mode 100644 index 000000000..d1adaa0a8 --- /dev/null +++ b/courses/rust_essentials/standard_course.txt @@ -0,0 +1 @@ +010_rust_essentials.rst