619 lines
13 KiB
Rust
619 lines
13 KiB
Rust
/*
|
|
|
|
# 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();
|
|
}
|