commit 98c7c05429766806b233db7c662dc2eb01bbe0de Author: Julien Palard Date: Wed Nov 15 10:00:58 2023 +0100 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..357b6ed --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "jour1" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d3b90f3 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "jour1" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/README.md b/README.md new file mode 100644 index 0000000..12f968c --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Mes notes sur 3 jours de rust diff --git a/src/bench.rs b/src/bench.rs new file mode 100644 index 0000000..a2ee0a6 --- /dev/null +++ b/src/bench.rs @@ -0,0 +1,13 @@ +// #![feature(test)] +// +// fn foo() -> i64 { +// 1 + 1 +// } +// +// #[bench] +// fn bench_add_two(b: &mut Bencher) { +// b.iter(|| foo()); +// } +// +// fn main() {} +// diff --git a/src/doc.rs b/src/doc.rs new file mode 100644 index 0000000..107d013 --- /dev/null +++ b/src/doc.rs @@ -0,0 +1,17 @@ +// cargo doc --open +// --open to open the browser. + +/// # The doc is in Markdown +/// +/// +/// - Use `///` for item comments +/// - Use `//!` for module comments +/// +/// Examples are doctests: +/// +/// ``` +/// let d1 = Duration::from_secs(60); +/// ``` +/// + +pub fn main() {} diff --git a/src/exercises.rs b/src/exercises.rs new file mode 100644 index 0000000..3d8883f --- /dev/null +++ b/src/exercises.rs @@ -0,0 +1,19 @@ +fn print_fourty_two() { + println!("42"); +} + +fn seconds_in_a_year() { + println!("Seconds in a year: {}", 60 * 60 * 24 * 365); +} + +fn approx_pi() { + let pi: f64 = 8958937768937. / 2851718461558.; + + assert!(pi > 3.0); +} + +pub fn main() { + print_fourty_two(); + seconds_in_a_year(); + approx_pi(); +} diff --git a/src/jour1.rs b/src/jour1.rs new file mode 100644 index 0000000..663e238 --- /dev/null +++ b/src/jour1.rs @@ -0,0 +1,618 @@ +/* + +# Les commentaires + +Les commentaires qui commencent par `///` ou `//!` sont réservés pour la doc. + +*/ + +// Les types primitifs + +// Types simples + +fn integers() { + let _x = 42i8; + let _x = 42i16; + let _x = 42i32; + let _x = 42u8; + let _x = 42u16; + let _x = 42u32; + let _x = 42isize; // Architecture dependent + let _x = 0x2a; + let _x = 0o52; + let _x = 100_000; +} + +fn floats() { + let _x = 12.5; // By default it's f64 +} + +fn bools() { + let a_boolean: bool = true; + + assert!(a_boolean == true); + assert!(false != true); +} + +fn chars() { + let unicode_codepoint = '√'; + + assert!(unicode_codepoint == '√'); +} + +// Container types + +fn tuples() { + let tuple = (1.0, 2.0, 10i64); + let partially_inferred: (_, i32, i32) = (10, 10, 12); + let tuple2 = (tuple.0 * 10.0, tuple.1 * 10.0); + + assert!(tuple.0 == 1.0); + assert!(tuple.2 == 10); + assert!(partially_inferred.0 == 10); + assert!(tuple2.0 == 10.0); +} + +fn arrays() { + // Can't mix types in arrays. + let inferred = [42, 42, 42]; // Inferres i32, i32, i32 + let tab: [u64; 4] = [42, 42, 42, 42]; + + assert!(tab[0] == 42); + assert!(inferred[0] == 42); +} + +fn a_function(x: i64) -> i64 { + // On function parameters, types are mandatory. + assert!(x == x); // It has a semicolon, it's an instruction. + x * 2 // Implicit return, no semicolon (an expression) + // The return keyword is unused. + // Rust does not like early returns ☹ +} + +fn variables() { + // Each variable has a type. + // But the compiler is great at inferring, fully or partially. + let a = 42; // Typically here we don't tell the type. + + a_function(a); // Forces a to be inferred as i64 as a_function excepts an i64. +} + +fn mutable_variables() { + // Variables are immuable by default. + let mut a = 42; // Can be rebound. + + assert!(a == 42); + a = 43; // Rebinding + assert!(a == 43); +} + +// Used by const PI, executed AT COMPILE TIME +const fn compute_pi() -> f64 { + 3.14159265358979323846 +} + +const PI: f64 = compute_pi(); // Computed at compile time!! + +fn annotations() { + #[allow(non_snake_case)] + // Compiler won't whine about the bad casing. + let Foo = 42; + + assert!(Foo == 42); +} + +// rustfmt : Formatte un fichier +// cargo fmt : Formatte un projet + +fn masquage() { + let a = 42; + { + let a = 43; + assert!(a == 43); + } + assert!(a == 42); +} + +fn mutable_tuple() { + // Tuples can be mutable. + let mut mutuple = (1, 2, 3); + + mutuple.1 = 0; + assert!(mutuple.1 == 0); +} + +fn blocs_has_value() { + let pi = { + let a = 3.0; + let b = 0.141592; + a + b + }; + assert!(pi > 3.0); +} + +fn the_if_instruction() { + let number = 3; + + if 2 < number && number < 5 { + assert!(true); + } else { + assert!(false); + } +} + +fn the_if_expression() { + let condition = true; + let number = if condition { 5 } else { 6 }; + + assert!(number == 5); +} + +fn in_fact_if_is_always_an_expression() -> i64 { + let condition = true; + + if condition { + 0x42i64 + } else { + 0x21i64 + } +} + +// fn the_loop_loop() { +// loop { +// println!("Will print at nauseum"); +// } +// } + +fn breaking_the_loop_loop() { + let mut count = 10; + + loop { + count -= 1; + if count == 0 { + break; + } + } + // Loops can be named using: + // 'name loop { ... +} + +fn break_can_take_value() -> i32 { + let mut count = 10; + let result = loop { + count += 1; + if count == 20 { + break count * count; + } + }; + result +} + +fn loop_is_an_expression() -> i64 { + let mut count: i64 = 10; + + loop { + count -= 1; + if count < 5 { + break count; + } + } +} + +fn while_loop() { + let mut count = 10; + + while count > 0 { + count -= 1; + } +} + +fn for_loop() { + for i in 0..3 { + // 0, 1, 2 + assert!(i < 10); + } + for j in 0..=3 { + // 0, 1, 2, 3 + assert!(j < 10); + } + let array = [1, 2, 3]; + for k in array { + assert!(k > 0); + } +} + +fn exercise_1_to_the_power_of_funky(mut n: i64, mut p: i64) -> i64 { + let value = n; + + loop { + if p <= 1 { + break n; + } + n *= value; + p -= 1; + } +} + +fn power(n: i64, p: i64) -> i64 { + let mut value = n; + + for _ in 1..p { + value *= n; + } + value +} + +fn as_keyword() -> i64 { + let n = 42i8; + + 99i64 * (n as i64) +} + +use std::time::Duration; +fn struct_types() { + let five_seconds = Duration::new(5, 0); // new is just a normal function. + + assert!(five_seconds.as_secs_f64() == 5.0); // This is some kind of maybe a method. +} + +fn enumerated_types() { + // "some types" "enums" + // The most common is "Option". + // Option is usefull because `null` does not exists in rust. + // + // pub enum Option { + // None, + // Some(T), + // } + let n = Some(2); + + assert!(!n.is_none()); + assert!(n.is_some()); + + assert!(n.unwrap() == 2); + + match n { + Some(k) => { + assert!(k == 2); + } + None => { + assert!(false); + } + } +} + +// Enum are enumerated named values, not enumerated types. Types are lost at +// runtime (type erasure). + +enum Test { + A(T), + B(T), + C(T), +} + +fn play_with_enum(n: Test) { + match n { + Test::A(_a) => { + // do something... + } + Test::B(_b) => { + // do something... + } + Test::C(_c) => { + // do something... + } + } +} + +fn f_strings() { + let n = 42; + + println!("La réponse à la question est {n}"); // like Python f-strings. + println!("La réposne à la question est {}", n); + + let m = dbg!(n) + dbg!(n); // dbg! returns given value (not println!). + assert!(m == 84); +} + +// Macros : functions ending with bangs. + +fn display_difference(dur: Option) { + match dur { + None => { + println!("No duration."); + } + Some(n) => { + println!("Duration: {n:?}") + } + } +} + +fn durées() { + let d1 = Duration::from_secs(60); + let d2 = Duration::from_secs(3600); + + assert!(d2.saturating_sub(d1) == Duration::from_secs(3540)); + assert!(d1.saturating_sub(d2) == Duration::from_secs(0)); + display_difference(d1.checked_sub(d2)); + display_difference(d2.checked_sub(d1)); +} + +fn ownership() { + // En dehors des références et des pointeurs : + // chaque valeur connaît son propriétaire (le "propriétaire" c'est une variable). + let mut s = String::from("hello"); // Sur la heap car taille variable. + + s.push_str(", world!"); + assert!(s == "hello, world!"); +} + +// fn moving_ownership() { +// let s1 = String::from("hello"); +// let s2 = s1; // Ownership transfer +// dbg!(s1); // Error, s1 is no longer owner of the hello string. +// // The same happen when we call a function: giving an argument is giving ownership. +// There's a clone() method to deep copy data, so we can have two ownerships. +// If it's a number instead of a string, there's no issue, because it's a primitive type allocated on the stack. + +// if it's on the stack : copy is implicit (they implement the Copy trait). +// if it's on the heap : copy has to be explicit. +// } + +fn len(s: &String) -> usize { + s.len() +} + +fn change(s: &mut String) { + // This is a mutable reference + // There can be only one mutable reference to a value. + s.push_str(".") +} + +fn borrowing() { + // It's like pointers but it is called references. + let mut s = String::from("Hello"); + let len = len(&s); + + assert!(len == 5); + change(&mut s); + assert!(s == "Hello."); +} + +fn slices() { + let s = String::from("Hello world"); + let hello = &s[0..5]; // A reference to a slice of the string. + let world = &s[6..11]; // Another reference to another slice of the string. + + assert!(hello == "Hello"); + assert!(world == "world"); +} + +fn their_concat(mut s1: String, s2: String) -> String { + // It's not a copy, it's ownership change. + s1.push_str(&s2); + s1 +} + +fn concat(s1: &String, s2: &String) -> String { + let mut s3 = String::from(""); + + s3.push_str(s1); + s3.push_str(s2); + s3 +} + +// &str is a string slice, or a static string in the binary (are they really the same?) +// String is a string object, in the heap. + +fn concat2(s1: &mut String, s2: &String) { + s1.push_str(s2); +} + +fn concat3(s1: &str, s2: &str) -> String { + // Those are string slices + format!("{}{}", s1, s2) +} + +fn repeat_str(s1: &str, times: usize) -> String { + let mut result = String::from(""); + for _ in 0..times { + result.push_str(s1); + } + result +} + +fn exercise3() { + let hello = String::from("Hello"); + let world = String::from(" world!"); + let hello_world = concat(&hello, &world); + assert!(hello_world == "Hello world!"); + + let mut hello2 = String::from("Hello"); + let world2 = String::from(" world!"); + concat2(&mut hello2, &world2); + assert!(hello2 == "Hello world!"); + + let hello3 = String::from("Hello"); + let world3 = String::from(" world!"); + assert!(concat3(&hello3, &world3) == "Hello world!"); + + let space = " "; + let spaces = repeat_str(space, 4); + assert!(spaces == " "); + + let their_hello = String::from("Hello"); + let their_world = String::from(" world!"); + // Funnily enough, their_hello is not mut, but it will be mutated by their_concat. + assert!(their_concat(their_hello, their_world) == "Hello world!"); + // Which is NOT bad in any way because their_hello is GIVEN to their_concat + // we can no longer use it, so we don't really care that it's mutated. +} + +// Creating types + +type Point1D = (f64, String); + +struct Point2D { + name: String, + x: f64, + y: f64, +} + +fn format_point(p: Point1D) -> String { + format!("Point({}, {:?})", p.0, p.1) +} + +fn add_points1d(p1: &Point1D, p2: &Point1D) -> Point1D { + (p1.0 + p2.0, format!("{} + {}", p1.1, p2.1)) +} + +struct Point4D(f64, f64, f64, f64); // Behave like a tuple. + +// A struct is allowed to have no fields, may be usefull to "store" methods in it. + +fn test_points() { + let p1: Point1D = (0.0, "Origin".to_owned()); + let p2: Point1D = (10.0, "Somewhere".to_owned()); + + let p3 = add_points1d(&p1, &p2); + + assert!(p3.0 == 10.0); + assert!(format_point(p3) == "Point(10, \"Origin + Somewhere\")"); + + //dbg!(add_gpoints(&m1, &m2)); + + let p = Point2D { + name: String::from("origin"), // If I have a variable named + // `name`, just `name` would be enough instead of `name: name`. + x: 0.0, + y: 0.0, + // Also using `..p1` would get all values from p1, kind of **kwargs. + }; + println!("{}: {}, {}", p.name, p.x, p.y); + + let p4: Point4D = Point4D(0.0, 0.0, 0.0, 0.0); + assert!(p4.0 == 0.0); +} + +// Enums + +enum Color { + Red, + Green, + Blue, +} + +enum ProcessStatus { + InProgress, + Error(String), + Success { output: String, duration: Duration }, +} + +fn test_enum() { + let _color = Color::Red; + let _color = Color::Green; + let color = Color::Blue; + + match color { + Color::Blue => { + println!("Is blue"); + } + _ => { + println!("Is not blue"); + } + } + + let _p1 = ProcessStatus::InProgress; + let _p2 = ProcessStatus::Error(String::from("Timeout")); + let p3 = ProcessStatus::Success { + duration: Duration::from_secs(25), + output: String::from("Success"), + }; + + match p3 { + // Match can has guards exactly like in Python + ProcessStatus::InProgress => { + println!("In progress..."); + } + ProcessStatus::Error(e) => { + println!("Errorred {}", e); + } + ProcessStatus::Success { + duration: d, + output: o, + } => { + println!("Done in {:?} seconds: {}", d, o); + } + } +} + +// The if let + +fn test_if_let() { + let p = ProcessStatus::Error(String::from("Timeout")); + + match p { + ProcessStatus::Error(ref e) => println!("Process failed: {e}"), + _ => (), + } + + // same as + + if let ProcessStatus::Error(ref e) = p { + println!("Process failed: {e}"); + } +} + +// No return annotation, inferred as an empty tuple, some kind of "void". +pub fn main() { + println!("Hello World"); + bools(); + chars(); + integers(); + floats(); + tuples(); + arrays(); + a_function(0x42i64); + variables(); + mutable_variables(); + assert!(PI > 3.0); + annotations(); + masquage(); + blocs_has_value(); + mutable_variables(); + mutable_tuple(); + the_if_instruction(); + the_if_expression(); + assert!(in_fact_if_is_always_an_expression() == 0x42); + breaking_the_loop_loop(); + break_can_take_value(); + assert!(loop_is_an_expression() == 4); + while_loop(); + for_loop(); + + let n = 2; + let r = 10; + assert!(exercise_1_to_the_power_of_funky(n, r) == 1024); + assert!(power(power(3, 3), 3) == 19683); + assert!(power(power(power(3, 3), 3), 3) == power(3, power(3, 3))); + // dbg!(power(3, power(3, power(3, 3)))); // overflow + as_keyword(); + struct_types(); + enumerated_types(); + let test: Test = Test::A(1); + play_with_enum(test); + let test: Test = Test::B(1); + play_with_enum(test); + let test: Test = Test::C(1); + play_with_enum(test); + f_strings(); + durées(); + ownership(); + borrowing(); + slices(); + exercise3(); + test_points(); + test_enum(); + test_if_let(); +} diff --git a/src/jour2.rs b/src/jour2.rs new file mode 100644 index 0000000..379939f --- /dev/null +++ b/src/jour2.rs @@ -0,0 +1,235 @@ +type Point1D = (T,); +type Point2D = (T, T); +type Point3D = (T, T, T); + +trait Signed { + fn abs(&self) -> Self; +} + +trait Sqrtable { + fn sqrt(&self) -> Self; +} + +trait Distance { + fn from_origin(&self) -> T; +} + +impl Distance for Point1D { + fn from_origin(&self) -> T { + self.0.abs() + } +} + +impl + std::ops::Add + Sqrtable + std::marker::Copy> + Distance for Point2D +{ + fn from_origin(&self) -> T { + (self.0 * self.0 + self.1 * self.1).sqrt() + } +} + +fn add_points_3d + std::marker::Copy>( + p1: &Point3D, + p2: &Point3D, +) -> Point3D { + (p1.0 + p2.0, p1.1 + p2.1, p1.2 + p2.2) +} + +// fn largest(list: &[T]) -> &T { // &[T] is a T iterable. +//} + +// The debug trait +// #[derive(Debug)] // This is called an attribute, it's like annotations. +// struct Rectangle { +// width: u32, +// height: u32, +// } + +// There also exist a std::fmt::Display trait, with a required `fmt` function. + +// There is also a #[derive(Clone)], we may put in on almost every +// types, as long as it make sense. + +// Copy is just a flag telling "The clone is virtually free". + +// We can combine, like #[derive[Clone, Debug)] + +fn play_with_generics_and_lifetime() { + // Generics are fully resolved at compile-time. There's 0 cost at runtime. + let m1: Point3D = (10, 20, 30); + let m2: Point3D = (1, 2, 3); + + let m3 = add_points_3d(&m1, &m2); + assert!(m3.0 == 11); +} + +// PartialEq implement == and != +// Eq is a flat telling that a value is equal to self +// So floats implement PartialEq but no Eq +// Many other things can be flagged Eq + +// impl c'est cool + +// Traits: + +// trait Area { +// fn area(&self) -> u32; // ends with `;`, it's not implemented. +// fn is_big(&self) -> bool { self.area() > 5 } // it's implemented ! +//} + +// impl Area for Rectangle { +// // Here I have to implement at least all unimplemnted methods from trait. +//} + +// From et Into are traits to convert from types to other types. + +// Lifetime + +// lifetime are annotated using simple quote followed by a letter. +// It looks like parameter types, like <'a>: + +fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { + // L'entrée et la sortie doivent partager les mêmes contraintes de durée de vie. + // La durée de vie la plus courte est choisie. + if x.len() > y.len() { + x + } else { + y + } +} + +#[derive(Debug)] +struct IPossesTheString { + name: String, +} + +#[derive(Debug)] +struct IDontPossesIt<'a> { + // La string doit survivre à la structure + // 'a : IDontPossesIt instances should live "longer" + name: &'a str, // than this string. +} + +// See elision of lifetimes +// See 'static + +fn is_in(v: &Vec<&str>, needle: &str) -> bool { + for s in v { + if *s == needle { + return true; + } + } + false +} + +fn print_first_item(list: &[T]) { + // Here list is "an iterable". + dbg!(&list[0]); +} + +struct Fib { + a: u64, + b: u64, +} + +impl Fib { + fn new() -> Self { + Self { a: 1, b: 1 } + } +} + +impl Iterator for Fib { + type Item = u64; + + fn next(&mut self) -> Option { + (self.a, self.b) = (self.b, self.a + self.b); + Some(self.a) + } +} + +pub fn main() { + println!("Hello World"); + play_with_generics_and_lifetime(); + let string1 = String::from("abcd"); + let string2 = "yux"; + + let result = longest(string1.as_str(), string2); + println!("The longest string is {result}"); + + let a = IPossesTheString { + name: "Hello".to_owned(), + }; + let b = IDontPossesIt { name: "Hello" }; + println!("{}", a.name); + println!("{}", b.name); + + // lambdas: + + let f = |n1, n2| n1 + n2; + assert!(f(1, 2) == 3); + + // Vec: + + let v: Vec = Vec::new(); + + assert!(v.get(0) == None); + + let with_initial_values = vec![1, 2, 3]; + assert!(with_initial_values[0] == 1); + + // Vectors are muables: + + let mut v = vec![1, 2, 3]; + v.push(4); + v.push(5); + + assert!(v[0] == 1); + + let second = v[1]; + assert!(second == 2); + + let v: Vec<&str> = vec!["Hello", "world"]; + assert!(is_in(&v, "Hello")); + assert!(!is_in(&v, "Pouette")); + + // Slices + + let mut v = vec![1, 2, 3, 4, 5, 6]; + //dbg!(&v); + //dbg!(v.as_slice()); + let s = &mut v[1..=2]; // "MemoryView" + s[0] = 0; + let v = vec![10, 20, 30]; + assert!(v[0] == 10); + print_first_item(&v); + + // HashSet + + let mut set = std::collections::HashSet::new(); + set.insert("Hello"); + set.insert("World"); + assert!(set.contains("Hello")); + assert!(!set.contains("Boo")); + + // HashMap + + let mut dict = std::collections::HashMap::new(); + dict.insert("Key", "Value"); + assert!(dict.contains_key("Key")); + assert!(dict["Key"] == "Value"); + + for (key, value) in &dict { + assert!(key == &"Key"); + assert!(value == &"Value"); + } + + // Iterators (it's a trait) + let fib = Fib::new(); + for i in fib { + if i > 1000 { + break; + } + } + let fib10 = Fib::new().take(10).collect::>(); + assert!(fib10[5] == fib10[4] + fib10[3]); +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..159457f --- /dev/null +++ b/src/main.rs @@ -0,0 +1,18 @@ +mod bench; +mod doc; +mod exercises; +mod jour1; +mod jour2; +mod rpg; +mod starwars; +mod tests; + +fn main() { + jour1::main(); + exercises::main(); + jour2::main(); + rpg::main(); + starwars::main(); + tests::main(); + doc::main(); +} diff --git a/src/rpg.rs b/src/rpg.rs new file mode 100644 index 0000000..366b00c --- /dev/null +++ b/src/rpg.rs @@ -0,0 +1,150 @@ +use std::fmt; + +#[derive(Debug, Clone, PartialEq, Eq)] +enum Class { + Paladin, + Mage { mana: i64 }, + Thief, +} + +impl std::fmt::Display for Class { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + Class::Paladin => { + write!(f, "Paladin") + } + Class::Mage { mana } => { + write!(f, "Mage(mana={})", mana) + } + Class::Thief => { + write!(f, "Thief") + } + } + } +} + +#[derive(Debug, Clone, Eq)] +struct Character { + name: String, + hp: u64, + class: Class, +} + +impl std::fmt::Display for Character { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(f, "{}: {} with {} hp", self.name, self.class, self.hp) + } +} + +impl std::cmp::PartialEq for Character { + fn eq(&self, other: &Self) -> bool { + self.name == other.name && self.class == other.class + } +} + +impl std::cmp::Ord for Character { + fn cmp(&self, other: &Character) -> std::cmp::Ordering { + self.name.cmp(&other.name) + } +} + +impl std::cmp::PartialOrd for Character { + fn partial_cmp(&self, other: &Character) -> Option { + Some(self.cmp(other)) + } +} + +impl Character { + fn new(name: &str) -> Self { + Self { + name: name.to_owned(), + hp: 100, + class: Class::Paladin, + } + } + + fn heal(&mut self, hp: u64) { + print!("Before healing:"); + self.print(); + self.hp = (self.hp + hp).min(100); + print!("After healing:"); + self.print(); + } + + fn paladin(name: &str) -> Self { + Character::new(name) + } + + fn thief(name: &str) -> Self { + let mut character = Self::new(name); + character.class = Class::Thief; + character + } + + fn mage(name: &str) -> Self { + let mut character = Self::new(name); + character.class = Class::Mage { mana: 100 }; + character + } + + fn print(&self) { + match self.class { + Class::Paladin => { + println!("Paladin {} ({} hp)", self.name, self.hp); + } + Class::Mage { mana } => { + println!("Mage ({}) {} ({} hp)", mana, self.name, self.hp); + } + Class::Thief => { + println!("Thief {} ({} hp)", self.name, self.hp); + } + } + } + + fn cast_spell(&mut self, other: &mut Character) { + if let Class::Mage { mana } = self.class { + if mana > 25 { + println!("{} casting a spell to {}", self.name, other.name); + other.hp = (other.hp - 25).max(0); + self.class = Class::Mage { mana: mana - 25 }; + } else { + println!("Has not enoug mana!"); + } + other.hp -= 50; + } else { + println!("{} cannot cast spell!", self.name); + self.hp = (self.hp - 5).max(0); + } + } +} + +// impl Class { +// fn mana(&self) -> Option<&i64> { +// match self { +// Self::Mage { mana } => Some(mana), +// _ => None, +// } +// } +// } + +fn test_rpg() { + let mut ada = Character::mage("Ada"); + let mut alan = Character::paladin("Alan"); + let mut windark = Character::thief("Windark"); + + ada.heal(0); + + ada.cast_spell(&mut alan); + alan.cast_spell(&mut ada); + ada.cast_spell(&mut windark); + windark.cast_spell(&mut ada); + + //println!("{}", ada); + //println!("{}", alan); + //println!("{}", windark); + //dbg!(ada); +} + +pub fn main() { + test_rpg(); +} diff --git a/src/starwars.rs b/src/starwars.rs new file mode 100644 index 0000000..6795ae3 --- /dev/null +++ b/src/starwars.rs @@ -0,0 +1,80 @@ +#[derive(Debug, Clone)] +struct Character { + name: String, + height: i32, + mass: i32, +} + +impl Character { + fn bmi(&self) -> f64 { + (self.mass as f64) / ((self.height as f64) / 100.0).powf(2.0) + } +} + +pub fn main() { + let characters = vec![ + Character { + name: "Luke Skywalker".to_owned(), + height: 172, + mass: 77, + }, + Character { + name: "C-3PO".to_owned(), + height: 167, + mass: 75, + }, + Character { + name: "R2-D2".to_owned(), + height: 96, + mass: 32, + }, + Character { + name: "Darth Vader".to_owned(), + height: 202, + mass: 136, + }, + Character { + name: "Leia Organa".to_owned(), + height: 150, + mass: 49, + }, + Character { + name: "Owen Lars".to_owned(), + height: 178, + mass: 120, + }, + Character { + name: "Beru Whitesun lars".to_owned(), + height: 165, + mass: 75, + }, + Character { + name: "R5-D4".to_owned(), + height: 97, + mass: 32, + }, + Character { + name: "Biggs Darklighter".to_owned(), + height: 183, + mass: 84, + }, + Character { + name: "Obi-Wan Kenobi".to_owned(), + height: 182, + mass: 77, + }, + ]; + + let mut with_bmi = characters + .iter() + .map(|c| (&c.name, c.bmi())) + .collect::>(); + with_bmi.sort_by_key(|tpl| ((tpl.1 * 1000.0) as u64)); + //dbg!(&with_bmi); + match with_bmi.last() { + None => {} + Some(character) => { + println!("{} has the biggest BMI", character.0); + } + } +} diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..3c94483 --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,125 @@ +// Crates +// ====== +// +// A library or executable, it has a root module, one of: +// src/main.rs : Contains the main function. +// src/lib.rs : Contains exposed entities as a library. +// +// +// Modules +// ======= +// +// A tree of code. Controls visibility. +// +// my_module.rst is "importable" using `mod my_module;` +// In a directory, mod.rs is like __init__.py in Python. +// +// `mod` imports a module (tells the compiler that the file exists). +// `use` imports a thing (fn, struct, ...), like a shortcut. +// use can be used like `use ... as ...`. +// use can be used as `use my_module::{..., ...}`. +// `pub use` imports and make public. +// +// +// Tooling +// ======= +// +// rustup : tool to install all the tools +// rustup update // updates all the tools +// +// cargo new my_project +// cargo new my_project --lib +// cargo clean +// cargo build --release +// cargo run --release +// cargo check +// cargo install +// +// In a Cargo.toml there's a package.edition field. It's for retro-compatibility. +// +// There's a [dependencies] dict. +// +// +// Features +// ======== +// +// It's for compilation flags +// It's a 'features' table in the toml file: +// +// [features] +// my_feat = [] +// +// Uses as: +// +// #[cfg(feature = "my_feat")] +// fn hello() {} +// +// #[cfg(not(feature = "my_feat"))] +// fn hello() {} +// +// There's a special `default` feature. +// +// There's optional dependencies: +// +// [dependencies] +// rand = { version = "0.8.5", optiona = true } +// // rand is only included if feature rand is active +// +// [features] +// my_feat = ["rand"] // Activate the optional dependency +// +// Or: +// +// [dependencies] +// rand = { version = "0.8.5", features = ["log"] } +// +// Linters +// ======= +// +// cargo clippy +// cargo clippy --all --all-targets --all-features -- -D warnings +// +// https://rust-lang.github.io/rust-clippy/master/index.html +// +// +// Tests +// ===== +// +// See at the end of this file. Integration tests (not included at +// compile time) can be stored in a tests/ directory near the src/ +// directory. They are also ran by `cargo test`. +// +// See also ntest, mockito, proptest. +// +mod my_module { + pub fn hello() { + println!("Hello"); + } + + pub mod nested_module { + pub fn bar() { + println!("Glou"); + } + } +} + +fn add>(x: T, y: T) -> T { + x + y +} + +pub fn main() { + my_module::hello(); + my_module::nested_module::bar(); + assert!(add(0.1, 0.2) == 0.2 + 0.1); +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn it_works() { + let x = 5; + assert!(add(x, x) == 10); + } +}