Skip to content
Snippets Groups Projects
Commit 63b49cc1 authored by orestis.malaspin's avatar orestis.malaspin Committed by Michaël El Kharroubi
Browse files

Adds summary text for part00

parent d9d9751f
No related branches found
No related tags found
No related merge requests found
...@@ -2,6 +2,14 @@ ...@@ -2,6 +2,14 @@
# https://hub.docker.com/r/library/rust/tags/ # https://hub.docker.com/r/library/rust/tags/
image: "rust:1.70-alpine3.17" image: "rust:1.70-alpine3.17"
# Use cargo to test the project # Use cargo to test the project
before_script:
- apk add --no-cache musl-dev curl
- mkdir -p $HOME/.cargo/bin
- which rustc
- which cargo
- curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.31/mdbook-v0.4.31-x86_64-unknown-linux-musl.tar.gz | tar -xz --directory=$HOME/.cargo/bin
- export PATH=$PATH:$HOME/.cargo/bin
test:cargo: test:cargo:
script: script:
- rustc --version && cargo --version # Print version info for debugging - rustc --version && cargo --version # Print version info for debugging
...@@ -10,3 +18,5 @@ run_test_doc: ...@@ -10,3 +18,5 @@ run_test_doc:
script: script:
- cd codes - cd codes
- ./run_tests.sh - ./run_tests.sh
- cd ../book
- mdbook build
book
[book]
authors = ["Orestis Malaspinas, Michaël El Kharroubi"]
language = "fr"
multilingual = false
src = "src"
title = "Rust-101: Université d'été"
# Summary
- [Part 00](./part00.md)
# Discussion du code `part00`
## Préambule
Ce court texte n'a pas vocation à remplacer un cours complet, mais à rappeler les concepts importants vus dans chaque partie du cours.
Les codes discutés ont tous pour but de calculer la valeur minimale d'entiers contenus dans un tableau. La difficulté
et l'élégance de ces codes ira en augmentant pour illustrer de façon itératives les différents concepts du présents
dans le langage.
### Installation du compilateur Rust
Afin d'installer le compilateur Rust, il n'est pas recommandé d'utiliser votre gestionnaire de paquet,
mais plutôt de télécharger toute la chaîne de compilation grâce à l'outil [rustup](https://www.rust-lang.org/tools/install).
Ou alors d'exécuter la commande suivante dans un terminal
```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```
Ce script installera pour vous le gestionnaire de paquet et de compilation `cargo`,
le compilateur `rustc`, ainsi que le linter `clippy` et l'outil de formatage de code
`rustfmt`.
Vous pouvez maintenant créer un nouveau projet rust (astucieusement nommé `new_project`) avec la commande
```bash
cargo new new_project
```
Cette commande crée un répertoire `new_project`, ainsi que les fichiers `new_project/Cargo.toml` et `new_project/main.rs`.
Vous pouvez compiler votre programme avec
```bash
cargo build
```
Puis l'exécuter à l'aide de la commande
```bash
cargo run
```
La commande cargo run dépend de l'étape de compilation, par conséquent si le code n'est pas compilé, alors la commande `cargo run` lancera la compilation avant d'exécuter votre programme.
Il est également possible de nettoyer les artéfacts de compilation ainsi que l'exécutable à l'aide de la commande
```bash
cargo clean
```
## Concepts
Les concepts abordés dans cet exemple sont:
1. [Les variables mutables ou non, les constantes.](#variables-variables-mutables-et-constantes)
2. [Les structures de contrôle `if ... else` et `for`.](#structures-de-contrôle)
3. [L'utilisation de tableaux statiques.](#structures-de-contrôle)
4. [L'utilisation de macros pour la gestion d'erreurs ou les sorties dans le terminal.](#macros)
## Discussion
Chaque code Rust a un unique point d'entrée: la fonction `fn main() {}`.
Ainsi, le code le plus simple (qui ne fait absolument rien) est.
```rust,no_run
fn main() {
}
```
Le corps de votre programme se trouvera donc entre les accolades.
### Variables, variables mutables, et constantes
Dans l'ordre d'apparition, nous avons d'abord une **constante** nommée SIZE, dont le type est `usize` (entier non signé dont la taille dépend de l'architecture, 8 octets sur une architecture 64 bits) et qui vaut `9`. Le nom du type vient après `:`.
```rust,no_run
const SIZE: usize = 9;
```
Ensuite nous déclarons un tableau statique (alloué sur la pile) d'entiers 32 bits et de taille `SIZE`.
```rust,no_run
# const SIZE: usize = 9;
let tab: [i32; SIZE] = [10, 32, 12, 43, 52, 53, 83, 2, 9];
```
Vous notez le mot clé `let` qui permet de déclarer une variable **immutables** (les valeurs contenues dans `tab` ou sa taille ne pourront plus changer). On dit qu'on **lie** (ou **bind** en anglais) `tab` à la valeur du `[10, 32, 12, 43, 52, 53, 83, 2, 9]`. Plus bas nous déclarons au contraire une variable **mutable** (qui elle pourra changer de valeur au cours de l'exécution du programme).
```rust
# const SIZE: usize = 9;
# let tab: [i32; SIZE] = [10, 32, 12, 43, 52, 53, 83, 2, 9];
let mut min = tab[0];
```
et l'initialisons avec la valeur du 1er élément de `tab` (ici la valeur est `10`).
### Structures de contrôle
Nous avons deux `if` dans ce code. Dans le premier
```rust,should_panic
const SIZE: usize = 0;
if SIZE == 0 {
panic!("Size is of tab = 0.");
}
```
nous testons si `SIZE == 0` et utilisons la macro `panic!` qui lorsqu'elle est exécutée fait quitter le programme et
affiche le message d'erreur en argument. Ainsi le code ci-dessus retourne:
```bash
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 0.56s
Running `target/debug/playground`
thread 'main' panicked at 'Size is of tab = 0.', src/main.rs:5:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```
Dans le second cas, nous sommes dans une boucle `for`
```rust,no_run
# const SIZE: usize = 9;
# let tab: [i32; SIZE] = [10, 32, 12, 43, 52, 53, 83, 2, 9];
let mut min = tab[0];
for i in 1..SIZE {
if min > tab[i] {
min = tab[i];
}
}
```
où l'indice `i` prend successivement les valeur `1` à `SIZE-1` (la notation `a..b` veut dire de `a` à `b` non inclus) et assignons
la valeur `tab[i]` à la *variable mutable* `min`. Si nous avions omis le mot clé `mut` lors de la déclaration de `min` l'assignation
donnerait une erreur (cliquez sur "play" pour la démonstration)
```rust,compile_fail
# const SIZE: usize = 9;
# let tab: [i32; SIZE] = [10, 32, 12, 43, 52, 53, 83, 2, 9];
let min = tab[0];
for i in 1..SIZE {
if min > tab[i] {
min = tab[i];
}
}
```
### Macros
Outre la macro `panic!()` nous utilisons ici deux macros permettant d'afficher des chaînes de caractère
dans le terminal. Les macros sont toujours identifiées à l'aide du `!` se trouvant à la fin de l'appel,
comme pour `panic!()`, `print!()` (affiche la chaîne de caractère en argument) ou `println!()` (qui est comme `print!()` mais
retourne à la ligne après avoir affiché). Comme on le voit dans les lignes suivantes
```rust
# const SIZE: usize = 9;
# let tab: [i32; SIZE] = [10, 32, 12, 43, 52, 53, 83, 2, 9];
# let mut min = tab[0];
# for i in 1..SIZE {
# if min > tab[i] {
# min = tab[i];
# }
# }
println!("Among the numbers in the list:");
for i in 0..SIZE {
print!("{} ", tab[i]);
}
println!();
```
le formatage de la ligne de caractère se fait à l'aide des accolades `{}` et les macros `print!()` / `println!()` prennent un nombre
d'arguments variables. A chaque `{}` doit correspondre une variable dont on veut afficher le contenu.
Il est également possible de numéroter chaque `{}`. Par exemple
```rust
println!("{1} {0}", "abc", "def");
```
Affichera `def abc`.
...@@ -123,7 +123,8 @@ fn read_command_line() -> [i32; SIZE] { ...@@ -123,7 +123,8 @@ fn read_command_line() -> [i32; SIZE] {
[10, 32, 12, 43, 52, 53, 83, 2, 9] [10, 32, 12, 43, 52, 53, 83, 2, 9]
} }
/// Prints all the elements of the `tab` and returns `tab`. /// Prints all the elements of the `tab`.
/// Tab is borrowed here
fn print_tab(tab: &[i32; SIZE]) { fn print_tab(tab: &[i32; SIZE]) {
for t in tab { for t in tab {
print!("{} ", t); print!("{} ", t);
...@@ -132,7 +133,7 @@ fn print_tab(tab: &[i32; SIZE]) { ...@@ -132,7 +133,7 @@ fn print_tab(tab: &[i32; SIZE]) {
} }
/// Computes the minimum of an Array of a type T which implements the [Minimum] trait. /// Computes the minimum of an Array of a type T which implements the [Minimum] trait.
/// Returns the array and a [SomethingOrNothing::Something] containing the the minimum value /// Returns a [SomethingOrNothing::Something] containing the the minimum value
/// or [SomethingOrNothing::Nothing] if no minimum value was found. /// or [SomethingOrNothing::Nothing] if no minimum value was found.
/// ///
/// # Example /// # Example
...@@ -145,20 +146,18 @@ fn print_tab(tab: &[i32; SIZE]) { ...@@ -145,20 +146,18 @@ fn print_tab(tab: &[i32; SIZE]) {
/// # } /// # }
/// ``` /// ```
fn find_min<T: Minimum>(tab: &[T; SIZE]) -> SomethingOrNothing<T> { fn find_min<T: Minimum>(tab: &[T; SIZE]) -> SomethingOrNothing<T> {
let mut min = SomethingOrNothing::Nothing; let mut minimum = SomethingOrNothing::Nothing;
// Here is T is not Copyable tab is consumed and cannot be returned // Here is T is Copyable. Which means that t is not moved in the loop
for t in tab { for t in tab {
min = min.min(SomethingOrNothing::Something(*t)); minimum = minimum.min(SomethingOrNothing::Something(*t));
} }
min minimum
} }
fn main() { fn main() {
let tab = read_command_line(); let tab = read_command_line();
println!("Among the Somethings in the list:"); println!("Among the Somethings in the list:");
print_tab(&tab); print_tab(&tab);
// There are alternatives to access fields of tuples // There are alternatives to access fields of tuples
let min = find_min(&tab); let min = find_min(&tab);
// The first field is not used therefore we can replace it with "_" // The first field is not used therefore we can replace it with "_"
......
use crate::minimum::Minimum;
/// Larger ints based on a [Vec] of [u8] to repensent arbitrary lengthy numbers.
/// The number has a sign as well.
pub struct BigInt {
/// The data contains the unsigned integers that are read from right to left
/// The number 1337 is stored as vec![7, 3, 3, 1]. Each number must be in the range [0,9]
/// and no trailing 0s are allowed.
data: Vec<u8>,
/// Contains the sign of the number +/-1;
sign: i8,
}
impl BigInt {
/// Tries to create a new [BigInt]. If the number is valid it returns
/// an Ok(BigInt) an Error otherwise.
///
/// # Examples
///
/// ```
/// use part08::big_int::BigInt;
/// let num = BigInt::try_new(vec![1, 2, 3, 4], 1);
/// assert!(num.is_ok());
/// let num = BigInt::try_new(vec![1, 2, 3, 4], -1);
/// assert!(num.is_ok());
/// let num = BigInt::try_new(vec![1, 2, 3, 4], 10);
/// assert!(num.is_err());
/// let num = BigInt::try_new(vec![1, 2, 3, 4], -10);
/// assert!(num.is_err());
/// ```
///
pub fn try_new(data: Vec<u8>, sign: i8) -> Result<Self, String> {
// EXERCISE:
// We don't check for trailing 0s but maybe this could be an exercise.
// Also we should check numbers are between 0 and 9.
if sign == 1 || sign == -1 {
Ok(BigInt { data, sign })
} else {
Err(String::from("Invalid sign."))
}
}
}
impl Clone for BigInt {
fn clone(&self) -> Self {
BigInt {
data: self.data.clone(),
sign: self.sign,
}
}
}
impl Minimum for BigInt {
// EXERCISE: Correct this function by using clippy and let it guide you.
// Get inspiration from Display to compute the Minimum
fn min(self, rhs: Self) -> Self {
if self.sign < rhs.sign {
return self;
} else if self.sign > rhs.sign {
return rhs;
}
if self.data.len() < rhs.data.len() {
return self;
} else if self.data.len() > rhs.data.len() {
return rhs;
}
for (l, r) in self.data.iter().rev().zip(rhs.data.iter().rev()) {
let ls = (*l as i8) * self.sign;
let rs = (*r as i8) * self.sign;
if ls < rs {
return self;
} else if ls > rs {
return rhs;
}
}
self
}
}
impl PartialEq for BigInt {
fn eq(&self, other: &Self) -> bool {
if self.sign == other.sign && self.data.len() == other.data.len() {
self.data
.iter()
.zip(other.data.iter())
.try_fold(true, |_, (l, r)| if l == r { Some(true) } else { None })
.is_some()
} else {
false
}
}
}
impl std::fmt::Display for BigInt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// This could be replaced by an `?`
if self.sign == -1 {
if let Err(e) = write!(f, "-") {
return Err(e);
}
}
// This could be replaced by an `?`
let res = self
.data
.iter()
.rev()
.try_fold((), |_, t| write!(f, "{}", t));
res
}
}
// EXERCISE: write tests
// EXERCISE: Modify Minimum trait to take references?
#[cfg(test)]
mod tests {}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment