Initial commit.
This commit is contained in:
commit
98c7c05429
|
@ -0,0 +1 @@
|
|||
/target
|
|
@ -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"
|
|
@ -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]
|
|
@ -0,0 +1,13 @@
|
|||
// #![feature(test)]
|
||||
//
|
||||
// fn foo() -> i64 {
|
||||
// 1 + 1
|
||||
// }
|
||||
//
|
||||
// #[bench]
|
||||
// fn bench_add_two(b: &mut Bencher) {
|
||||
// b.iter(|| foo());
|
||||
// }
|
||||
//
|
||||
// fn main() {}
|
||||
//
|
|
@ -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() {}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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]);
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue