Skip to content
Snippets Groups Projects
Commit a1412887 authored by orestis.malaspin's avatar orestis.malaspin
Browse files

Merge branch 'slides' into 'main'

Adding all existing chapters

Closes #29

See merge request !36
parents b5181d4d 5833c8e5
Branches
No related tags found
No related merge requests found
......@@ -6,12 +6,12 @@ SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
# Build the book first, because mdbook will create any empty sections
echo "Building book"
RUST_LOG=info mdbook build ${SCRIPT_DIR}
RUST_LOG=info mdbook build "${SCRIPT_DIR}"
# Then build the slides
echo "Building slides"
RUST_LOG=debug mdslides --template ${SCRIPT_DIR}/template.html --output-dir ${SCRIPT_DIR}/slides --mdbook-path ${SCRIPT_DIR} --index-template ${SCRIPT_DIR}/index-template.html
RUST_LOG=debug mdslides --template "${SCRIPT_DIR}/template.html" --output-dir "${SCRIPT_DIR}/slides" --mdbook-path "${SCRIPT_DIR}" --index-template "${SCRIPT_DIR}/index-template.html"
# TODO: move assets copying to mdslides
cp -r "${SCRIPT_DIR}/book/figs" "${SCRIPT_DIR}/slides"
# Then run the tests (which is slow)
echo "Testing book"
RUST_LOG=info mdbook test ${SCRIPT_DIR}
\ No newline at end of file
RUST_LOG=info mdbook test "${SCRIPT_DIR}"
\ No newline at end of file
......@@ -17,7 +17,9 @@
- [Traits](traits.md).
- [Tests](tests.md).
- [Vecteurs](vec.md).
- [Les Strings](string.md).
<!-- - [Itérateurs](iterators.md).
- [Unsafe Rust](collections.md).
- [Lifetimes](lifetimes.md). -->
- [Unsafe](unsafe.md).
# Les structures de contrôle
## Les branchements conditionnels
```rust [3-5|6-8|9-11|]
fn main() {
let x = 15;
if x < 10 {
println!("{} est plus petit que 10.", x);
}
else if x >= 10 && x < 20 {
println!("{x} est plus grand ou égal à 10 et plus petit que 20.");
}
else {
println!("{x} est plus grand ou égal à 20.");
}
}
```
## C'est des **expressions**
```rust [2|4-10|4,6,9,10|]
fn main() {
let x = -1;
let sign =
if x > 0 {
1 // pas de ;
} else if x < 0 {
-1 // pas de ;
} else {
0 // pas de ;
}; // attention au ;
}
```
## La boucle infinie: `loop`
```rust [] ignore
fn main() {
let mut x = 0;
loop {
println!("En boucle!");
x += 1;
}
}
```
## Sortie avec `break`
```rust [3,10|8|]
fn main() {
let mut i = 0;
let j = loop {
println!("{}, en boucle!", i);
i += 1;
if i == 10 {
println!("Fin de la boucle!");
break i; // i est optionnel
}
}; // attention au ;
}
```
## La boucle `while`
```rust [3,6|]
fn main() {
let mut i = 0;
while i != 10 {
println!("{}-ème boucle!", i);
i += 1;
}
println!("Fin de la boucle!");
}
```
## La boucle `for` et `continue`
```rust [2,7|3-5|]
fn main() {
for i in 0..10 {
if i % 2 == 0 {
continue;
}
println!("{}-ème boucle!", i);
}
println!("Fin de la boucle!");
}
```
## La boucle `for` ++
```rust ignore
for i in collection {
// instructions
}
```
slides/src/figs/ferris.png

46.2 KiB

slides/src/figs/mem_cp.png

62.9 KiB

slides/src/figs/mem_mv.png

50.2 KiB

slides/src/figs/mem_ref_vec.png

6.95 KiB

slides/src/figs/mem_vec.png

9.13 KiB

......@@ -36,7 +36,7 @@
- Une mascotte super mignonne
![Ferris](figs/ferris.svg)
![Ferris](figs/ferris.png)
## Une brève histoire du Rust
......
......@@ -46,20 +46,26 @@ fn main() {
} // x sort de la portée et sa valeur est détruite
```
## Allocation de la mémoire (1/3)
- Façons principales d'allouer/désallouer dynamiquement de la mémoire.
1. Manuellement (C/C++, ...): on ordonne à l'OS d'allouer/désallouer de la mémoire sur le tas.
a. Oublier de désallouer la mémoire allouée (fuite mémoire/memory leak).
b. Désallouer de la mémoire trop tôt (dangling pointer et comportement indéfini).
c. Libérer la mémoire à double.
2. Automatiquement: on a un "garbage collector" (java, scala, ...).
a. Consomme des ressources.
b. Est une "boîte magique": il fait ce qu'il veut.
## Allocation de la mémoire: Manuelle
* Manuellement (C/C++, ...): on ordonne à l'OS d'allouer/désallouer de la mémoire sur le tas.
- Oublier de désallouer la mémoire allouée (fuite mémoire/memory leak).
- Désallouer de la mémoire trop tôt (dangling pointer et comportement indéfini).
- Libérer la mémoire à double.
## Allocation de la mémoire: Garbage collection
* Automatiquement: on a un "garbage collector" (java, scala, ...).
- Consomme des ressources.
- Est une "boîte magique": il fait ce qu'il veut.
## Allocation de la mémoire: Rust
- En Rust on contrôle où et quand on alloue/désalloue à la compilation:
- Difficulté: il faut suivre des règles **très** strictes.
- Garbage collection "à la compilation".
## Allocation de mémoire (2/3)
## Allocation de mémoire (tas)
- Les types vus jusque là sont stockés dans la pile: leur *taille est connue* à la compilation.
- Que se passe-t-il lorsque la taille est *inconnue à la compilation*?
......@@ -76,9 +82,9 @@ fn main() {
} // x/y sortent de la portée, il sont détruits et la mémoire est libérée
```
## Allocation de mémoire (3/3)
## Allocation de mémoire (Vec)
![La représentation en mémoire du vecteur `v = (1,2,3,4)`](figs/mem_vec.svg)
![La représentation en mémoire du vecteur `v = (1,2,3,4)`](figs/mem_vec.png)
- Pile: 1 pointeur vers le tas, et 2 entiers (longueur et capacité).
- Tas: 1, 2, 3, 4.
......@@ -130,7 +136,7 @@ fn main() {
- En ne faisant rien on a **deux** propriétaires des données.
- Illégal: on invalide `y`.
![](figs/mem_mv.svg)
![](figs/mem_mv.png)
## Exception au `move`: `Copy`
......@@ -150,7 +156,7 @@ fn main() {
- *move*: copie uniquement la variable et le propriétaire **change**.
- *copie*: on duplique la variable **et** les données.
![](figs/mem_cp.svg)
![](figs/mem_cp.png)
## Quand interviennent les `move`?
......@@ -216,7 +222,7 @@ fn main() {
## La référence (schéma)
![](figs/mem_ref_vec.svg)
![](figs/mem_ref_vec.png)
## Exemple 1
......
......@@ -5,28 +5,33 @@
- Un **pointeur** est une variable qui contient une adresse mémoire.
- Cette adresse **pointe** vers des données.
![Illustration: tableau.](figs/mem_ref_vec.svg)
![Illustration: tableau.](figs/mem_ref_vec.png)
- Question: Quel type de pointeur avons-nous déjà rencontré?
- Réponse: La référence.
## Smart pointers
- Un **pointeur intelligent** est un type abstrait qui rajoute des fonctionnalités au poiteur standard.
- Type abstrait qui rajoute des fonctionnalités au poiteur standard.
- Management automatique de la mémoire.
- Vérification de limites.
- En particulier, ils permettent la désallocation de la mémoire de manière automatique:
- On peut avoir plusieurs pointeurs sur un espace mémoire.
- Quand le dernier pointeurs est détruit, l'espace mémoire est désalloué.
- Permet d'empêcher les fuites mémoires.
- Il existe différents types:
- Un pointeur unique est le propriétaire de ses données (quand il est détruit les données aussi).
- On compte le nombre de références sur des données, quand ce nombre tombe à zéro on détruit tout.
- Permettent la désallocation de la mémoire de manière automatique:
- Plusieurs pointeurs sur un espace mémoire.
- Dernier pointeurs est détruit => espace mémoire est désalloué.
- Empêche les fuites mémoires.
## Types de pointeurs
- Différents types:
- Pointeur unique, propriétaire de ses données (quand il est détruit les données aussi).
- Comptage de références sur des données, quand ce nombre tombe à zéro on détruit tout.
- Mutabilité intérieure: les règles de Rust sont imposées à l'exécution.
- Accès atomiques: pas de lecture/écriture concurrente possible sur la valeur pointée.
## En Rust
- Exemples:
- `Vec<T>`, `String`: ces types possèdent de la mémoire et la manipule eux-mêmes.
- `Vec<T>`, `String`: ces types possèdent de la mémoire et la manipulent eux-mêmes.
- Les pointeurs intelligent doivent implémenter deux traits:
- `Deref`: comment on déréférence le pointeur.
- `Drop`: comment on détruit le pointeur.
......
# Les chaînes de caractères
## Généralités
- Deux types de chaînes de caractères: `str` et `String`:
- `str`: *string literal* (stocké explicitement dans l'exécutable).
- `String`: liste dynamique de caractères UTF-8.
- `String` est propriétaire de ses données.
- `str` ne peut être "manipulé" que via `&str`
```rust
let literal = "Le type de literal est &str.";
```
## La structure `String`
```console
ptr # pointeur sur le tas de char
len # nombre d'éléments utilisés
capacity # mémoire allouée
```
## Créer des `String`s
```rust [1|2|3|]
let mut empty = String::new();
let hello = String::from("Hello World!"); // converti depuis un &str
let other_hello = "Hello World!".to_string();
```
## Modifier des `String`s
```rust [1-2|]
let mut s = String::from("Hello");
let s1 = " World!";
s.push_str(&s1); // On peut aussi faire push_str(" World!")
println!("{s}");
```
## Concaténer des `String`s
```rust [1-4|]
let s1 = String::from("Hello");
let s2 = String::from(" World!");
let sum1 = s1 + &s2; // s1 moved
let sum2 = "Hello".to_string() + &s2; // s2 borrowed
println!("{sum1} : {sum2}");
```
## Formatter des `String`s
```rust
let s1 = String::from("Hello");
let s2 = String::from("World");
let formatted = format!("{s1} {s2}!");
println!("{formatted}");
```
## Indexer des `String`
```rust compile_fail
let s = String::from("Hello");
let t = s[0]; // fail
```
```rust
let hello = String::from("Καλημέρα!");
println!("{}", hello.len());
```
## Encodage
* UTF-8 `!=` ASCII: encodage à longueur variable (1 à 4 octets).
* Impossible d'indexer en `O(1)`, car on connaît pas la valeur avant d'avoir lu 1 à 4 octets à chaque fois.
## Mais alors comment faire?
```rust [1-5|6|]
let hello = String::from("Καλημέρα!");
for c in hello.chars() {
print!("{c}");
}
println!("");
println!("index 3: {}", hello.chars().nth(3).unwrap());
```
## Le slice de `String`
* Une tranche de `String` est une référence vers un bout de chaîne d'UTF-8.
* On peut facilement créer des tranches de `String`... invalides.
```rust should_panic
let hello = String::from("Καλημέρα!");
let h = &hello[0..3];
```
......@@ -23,12 +23,12 @@ trait Animal { // définition d'un trait
} // le nombre de méthode est arbitraire
```
## Implémentation d'un trait pour un type (1/2)
## Implémentation d'un trait pour un type
- Tout type qui implémentera un trait devra implémenter **toutes** ses méthodes.
- Le compilateur saura que tout type implémentant un trait peut appeler ses méthodes.
- Un type qui implémente un trait doit implémenter **toutes** ses méthodes.
- Le compilateur sait qu'un type implémentant un trait peut appeler ses méthodes.
```rust
```rust [5-7|8-15|]
trait Animal { // définition d'un trait
fn cri(&self) -> String;
fn nom(&self) -> &String;
......@@ -50,9 +50,9 @@ fn main() {
}
```
## Implémentation d'un trait pour un type (2/2)
## Implémentation d'un trait pour deux types
```rust
```rust [15-17|18-25|]
trait Animal { // définition d'un trait
fn cri(&self) -> String;
fn nom(&self) -> &String;
......@@ -89,9 +89,9 @@ fn main() {
## Implémentations par défaut
- Il est pratique d'avoir des implémentations par défaut (les mêmes pour tous les types implémentant un trait).
- Pratique d'avoir des implémentations par défaut (les mêmes pour tous les types implémentant un trait).
```rust
```rust [1,3-5,6|]
trait Animal { // définition d'un trait
fn cri(&self) -> String;
fn cri_puissant(&self) -> String {
......@@ -110,7 +110,6 @@ impl Animal for Cat { // le bloc où les méthodes du trait sont implémentées
String::from("Miaou")
}
}
fn main() {
let chien = Dog{};
println!("Le cri du chien est {} et son cri puissant est {}.",
......@@ -126,13 +125,12 @@ fn main() {
- Lors de la définition d'un générique, on peut dire au compilateur si le type implémente un trait.
```rust
// Cette fonction ne peut pas foncitonner pour tous les types
```rust [1-5|7-9|10-12|]
// T implémente PartialEq (les opérateurs <, >)
fn max<T: PartialOrd>(a: T, b: T) -> T {
if a > b { a } else { b } // si on peut pas comparer a et b
} // cela ne peut pas compiler, d'où
// le PartialOrd
// le PartialOrd
fn main() {
let a = 1;
let b = 7;
......
# Unsafe Rust
* Compilateur **très pointilleux**:
* garanties de sécurité mémoire **très fortes**,
* décidées dès **la compilation**.
* Des fois le compilateur prend **trop de précautions**:
* Peut rejeter un code valide lors d'un doute,
* Rust `unsafe` donne des pouvoirs supplémentaires,
* Tant pis si l'utilisateur·trice les utilise mal.
* Les opérations de bas niveau sont `unsafe` par définition (p.ex. interactions avec l'OS).
# Nouveaux pouvoirs
* Déréférencer un pointeur brut,
* Appeler une fonction ou une méthode `unsafe`,
* Accéder à une variable statique mutable ou la modifier,
* Implémenter trait `unsafe`,
* Accéder aux champs des unions.
# Les blocs `unsafe`
```rust [1-3|4-6]
unsafe {
// code unsafe
};
let bla = unsafe {
// le code peut retourner une valeur
};
```
# Mais attention
* Les garanties mémoires sur les références disparaissent en mode `unsafe`
```rust compile_fail
let a = 12;
unsafe {
let b = &a;
let c = &mut a;
};
```
# Le type pointeur brut
```rust [1-2|3-4]
let a = 12;
let ptr_a: * const i32 = &a as * const i32;
let mut b = 24;
let mut_ptr_b: * mut i32 = &mut b as * mut i32;
```
# Propriétés des pointeurs bruts
* `* const T`/`* mut T`: on ne peut/peut pas modifier la valeur pointée,
* Aucune garantie que le mémoire pointée est valide,
* Pas de libération automatique de la mémoire pointée lors de la sortie de la portée,
* Possibilité d'allouer des données sur le tas: `alloc()` `dealloc()`.
# Déréférencement d'un pointeur brut
```rust [1-3|4-6]
let mut a = 12;
let ptr_a = &a as * const i32;
let mut mut_ptr_a = &mut a as * mut i32;
unsafe {
*mut_ptr_a = 20;
println!("{}, {}", *ptr_a, *mut_ptr_a);
}
```
# Fonction `unsafe`
```rust [1|2]
unsafe fn attention_not_safe() {
// everything in here is implicitly annotated unsafe
}
unsafe {
attention_not_safe();
}
```
# Remarques
* Garder le code `unsafe` aussi petit que possible,
* Aide à trouver où peuvent se trouver les erreurs de mémoire,
* Cacher le code `unsafe` dans une API `safe`,
* Permet de communiquer avec du code "externe" (des lib C),
* Annotation `unsafe` d'un bloc: fait tourner ce code c'est promis il marche,
* Annotation `unsafe fn`: attention cette fonction à des contraintes particulières.
......@@ -106,7 +106,7 @@ fn main() {
}
```
## Lecture d'éléments (2/N)
## Lecture d'éléments (1/3)
```rust compile_fail
struct Int(i32);
......@@ -117,7 +117,7 @@ fn main() {
}
```
## Lecture d'éléments (3/N)
## Lecture d'éléments (2/3)
```rust
fn main() {
......@@ -133,7 +133,7 @@ fn main() {
}
```
## Lecture d'éléments (4/N)
## Lecture d'éléments (3/3)
```rust
fn main() {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment