Initial commit.

This commit is contained in:
Julien Palard 2023-11-15 10:00:58 +01:00
commit 98c7c05429
Signed by: mdk
GPG Key ID: 0EFC1AC1006886F8
13 changed files with 1292 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

7
Cargo.lock generated Normal file
View File

@ -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"

8
Cargo.toml Normal file
View File

@ -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]

1
README.md Normal file
View File

@ -0,0 +1 @@
# Mes notes sur 3 jours de rust

13
src/bench.rs Normal file
View File

@ -0,0 +1,13 @@
// #![feature(test)]
//
// fn foo() -> i64 {
// 1 + 1
// }
//
// #[bench]
// fn bench_add_two(b: &mut Bencher) {
// b.iter(|| foo());
// }
//
// fn main() {}
//

17
src/doc.rs Normal file
View File

@ -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() {}

19
src/exercises.rs Normal file
View File

@ -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();
}

618
src/jour1.rs Normal file
View File

@ -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<T> {
// 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<T> {
A(T),
B(T),
C(T),
}
fn play_with_enum(n: Test<u64>) {
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<Duration>) {
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<u64> = Test::A(1);
play_with_enum(test);
let test: Test<u64> = Test::B(1);
play_with_enum(test);
let test: Test<u64> = Test::C(1);
play_with_enum(test);
f_strings();
durées();
ownership();
borrowing();
slices();
exercise3();
test_points();
test_enum();
test_if_let();
}

235
src/jour2.rs Normal file
View File

@ -0,0 +1,235 @@
type Point1D<T> = (T,);
type Point2D<T> = (T, T);
type Point3D<T> = (T, T, T);
trait Signed {
fn abs(&self) -> Self;
}
trait Sqrtable {
fn sqrt(&self) -> Self;
}
trait Distance<T> {
fn from_origin(&self) -> T;
}
impl<T: Signed> Distance<T> for Point1D<T> {
fn from_origin(&self) -> T {
self.0.abs()
}
}
impl<T: std::ops::Mul<Output = T> + std::ops::Add<Output = T> + Sqrtable + std::marker::Copy>
Distance<T> for Point2D<T>
{
fn from_origin(&self) -> T {
(self.0 * self.0 + self.1 * self.1).sqrt()
}
}
fn add_points_3d<T: std::ops::Add<Output = T> + std::marker::Copy>(
p1: &Point3D<T>,
p2: &Point3D<T>,
) -> Point3D<T> {
(p1.0 + p2.0, p1.1 + p2.1, p1.2 + p2.2)
}
// fn largest<T: std::cmp::PartialOrd>(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<i64> = (10, 20, 30);
let m2: Point3D<i64> = (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<T> 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<T: std::fmt::Debug>(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::Item> {
(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<f64> = 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::<Vec<_>>();
assert!(fib10[5] == fib10[4] + fib10[3]);
}

18
src/main.rs Normal file
View File

@ -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();
}

150
src/rpg.rs Normal file
View File

@ -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<std::cmp::Ordering> {
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();
}

80
src/starwars.rs Normal file
View File

@ -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::<Vec<_>>();
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);
}
}
}

125
src/tests.rs Normal file
View File

@ -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 <paquet>
//
// 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<T: std::ops::Add<Output = T>>(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);
}
}