Talk presented at Touraine Tech (https://touraine.tech/) on the 19th January 2023, with the following (FR) abstract:
S'il y a bien un langage de programmation qui a le vent en poupe, c'est Rust. Les arguments en sa faveur ne manquent pas : programmation sûre, performance d'exécution, écosystème vivant, etc. Il n'est même pas nécessaire d'y ajouter l'argument d'autorité "Linus Torvalds approuve" !
Comme toute personne curieuse, nous nous sommes lancés… pour se prendre de plein fouet la courbe d'apprentissage rude qui fait aussi la réputation du langage. Mais bon, nous sommes utilisateurs de Vi au quotidien depuis des années, il n'y a pas de raison qu'on n'arrive pas à coder en Rust !
Dans cette présentation, nous proposons une approche visuelle pour illustrer le plus simplement possible les points d'achoppement de la montée en compétence sur Rust… et vous verrez qu'en fait, c'est pas compliqué, on a juste été très mal habitués par les autres langages depuis des années. Notre but ? Que vous repartiez avec les bons outils pour écrire vos premiers petits programmes en Rust et ne paniquiez pas face à la volubilité du compilateur.
2. Rust sans (trop) roter du sang
Premiers mots…
Alexis « Horgix » Chotard
SRE chez
@Horgix
Horgix
Édouard Siha
Cloud-Native Dev chez
https://blog.wescale.fr/
4. —
Rust sans (trop) roter du sang
01 Anatomie de Ferris
1er
contact
Paradigmes procédural,
fonctionnel,
concurrent
Programmation système
Compilé
Sûreté
Performance
Typage fort, statique
5. —
01 HTTP headers
1er
contact
Rust sans (trop) roter du sang
use std::collections ::HashMap;
use reqwest::{blocking ::Response , Error};
use serde::Deserialize ;
#[derive(Deserialize , Debug)]
struct Headers {
headers: HashMap<String, String>,
}
fn main() {
let url: &str = "https://httpbin.org/headers" ;
let http_response : Result<Response , Error> = reqwest::blocking ::get(url);
match http_response {
Ok(response ) => {
let headers = response .json::<Headers>().unwrap();
println! ("Headers: {:?}", headers);
},
Err(_) => {println! ("Failed to call httpbin" );},
}
}
Petit programme qui :
- fait un appel HTTP synchrone
à httpbin,
- désérialise le résultat vers un
type approprié,
- affiche le résultat
… le tout de manière sûre (à une
exception volontaire près) et en
utilisant des bibliothèques usuelles
au passage.
Notre objectif : faire en sorte que
ce bout de code soit limpide pour
vous à la fin de la conférence !
6. —
01 Cargo
1er
contact
Rust sans (trop) roter du sang
L’outil intégré au langage pour… à peu près tout faire :
● Gestion des dépendances ;
● Cycle de de vie de développement ;
● Documentation ;
● Publication de bibliothèques ;
● Formatage automatique des fichiers source ;
● etc.
8. —
02 Problématiques
Gestion de la mémoire
Rust sans (trop) roter du sang
● Quelle quantité de mémoire allouer ?
● Comment initialiser la mémoire allouée ?
● Quand libérer la mémoire allouée ?
● Comment orchestrer les accès concurrents ?
● Comment faciliter la gestion ?
9. —
02 L’approche manuelle
Gestion de la mémoire
Rust sans (trop) roter du sang
Allocation Manuelle : fonctions malloc et compagnie
Libération Manuelle : fonction free
Initialisation Manuelle*
Contrôle de la
concurrence
Au besoin, manuellement (sémaphores, verrous, etc.)
Contrôle Maximal
Sûreté Minimale
Facilité Moyenne
10. —
02 L’approche déléguée
Gestion de la mémoire
Rust sans (trop) roter du sang
Allocation Déléguée : mot-clef new
Libération Déléguée : ramasse-miettes
Initialisation Valeurs par défaut*
Contrôle de la
concurrence
Au besoin, manuellement (sémaphores, verrous, etc.)
Contrôle Minimal
Sûreté Moyenne
Facilité Maximale
11. —
02 L’approche de Rust
Gestion de la mémoire
Rust sans (trop) roter du sang
Allocation Déléguée : sans mot-clé
Libération Systématique : à la perte de visibilité du possesseur
Initialisation Requise pour l’utilisation
Contrôle de la
concurrence
Systématique : intégrée au langage
Contrôle Maximal
Sûreté Maximale
Facilité Presque maximale
12. —
02 Possession et emprunt
Gestion de la mémoire
Rust sans (trop) roter du sang
🏠
👤 possède
13. —
02 Possession et emprunt
Gestion de la mémoire
Rust sans (trop) roter du sang
🏠
👤
emprunte en
« lecture seule » 👥
possède
14. —
02 Possession et emprunt
Gestion de la mémoire
Rust sans (trop) roter du sang
🏠
👤 possède
🛠
emprunte en
« écriture »
15. —
02 Possession et emprunt
Gestion de la mémoire
Rust sans (trop) roter du sang
🏠
👤
emprunte en
« lecture seule » 👥
possède
🛠
emprunte en
« écriture »
16. —
02 Possession et emprunt
Gestion de la mémoire
Rust sans (trop) roter du sang
🏠
👤 possède
👤
17. —
02 Possession et emprunt
Gestion de la mémoire
Rust sans (trop) roter du sang
🏠
👤 possède
👤
« déplace » la propriété
18. —
02 Possession et emprunt
Gestion de la mémoire
Rust sans (trop) roter du sang
🏠
👤 👤
possède
19. —
Rust sans (trop) roter du sang
02 … par l’exemple
Gestion de la mémoire
Explication live sur une animation vidéo de 5min36 !
Désolé, vous devrez attendre la diffusion
de l’enregistrement vidéo pour en profiter.
Stay tuned :
https://touraine.tech/
https://twitter.com/tourainetech
21. —
03 Pattern
matching
Les notions indispensables
Rust sans (trop) roter du sang
let x = 1;
match x {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
_ => println!("anything else"),
}
Permet de s’assurer de ne jamais
oublier de cas. Pas d’oubli, même à
3h du matin ou après 3 pintes !
22. —
03 Type
Result
Les notions indispensables
Rust sans (trop) roter du sang
let parsing_result = "Hello".parse::<i32>();
match parsing_result {
Ok(i: i32) => println!("Yay, success: {}", i),
Err(_) => println!("Oh no, an error!"),
}
Gérer les cas d’erreur :
● Proprement,
● En étant sûr de ne pas
oublier de les gérer,
● Avec des types appropriés.
Result<i32, ParseIntError>
23. —
03 Type
Option
Les notions indispensables
Rust sans (trop) roter du sang
let talk_audience = Some("Tours");
match talk_audience {
Some(audience: &str) => println!("Hello {}", audience),
None => println!("Damn, no one is here :("),
}
Parce que des fois, on a une valeur
mais… des fois, on en a pas.
Adieu les nil pointers pour
représenter l’absence consciente et
volontaire de valeur !
Option<&str>
24. —
03 Raccourci n°1
?
Les notions indispensables
Rust sans (trop) roter du sang
let i: i32 = "Hello".parse::<i32>()?;
// Strictement équivalent à :
let i: i32 = match "Hello".parse::<i32>() {
Ok(val: i32) => val,
Err(err: ParseIntError) => return Err(From::from(err)),
};
L’opérateur ? permet de retourner
directement une erreur, laissant
ainsi la responsabilité à l’appelant
de la traiter ou de la remonter à
son tour.
Result<i32, ParseIntError>
25. —
03 Raccourci n°2
.unwrap()
Les notions indispensables
Rust sans (trop) roter du sang
let talk_audience: &str = Some("Tours").unwrap();
// Strictement équivalent à :
let talk_audience: &str = match Some("Tours") {
Some(audience: &str) => audience,
None => panic!("No talk_audience"),
};
« Si, si, je suis sûr, j’ai une valeur, j’ai
pas None, promis ! »
Extrait directement la valeur d’un
type Option ou panic s’il n’y en a
pas.
Attention, danger ! Option<&str>
26. —
03 Enums
Les notions indispensables
Rust sans (trop) roter du sang
Elles ont l’air banales… mais pas
tant que ça.
enum TalkKind {
Conference,
Meetup,
TouraineTech,
}
27. —
03 Struct
Les notions indispensables
Rust sans (trop) roter du sang
enum TalkKind {
Conference,
Meetup,
TouraineTech,
}
struct Talk {
title: String,
kind: TalkKind,
audience: Option<String>,
}
La base des structures de données
en Rust.
28. —
03 Struct
Les notions indispensables
Rust sans (trop) roter du sang
enum TalkKind {
Conference,
Meetup,
TouraineTech,
}
struct Talk {
title: String,
kind: TalkKind,
audience: Option<String>,
}
let talk = Talk {
title: "Rust sans (trop) roter du sang".to_string(),
kind: TalkKind::TouraineTech,
audience: Some("Tours".to_string()),
};
La base des structures de données
en Rust.
29. —
03 Traits
Les notions indispensables
Rust sans (trop) roter du sang
let talk = Talk {
title: "Rust sans (trop) roter du sang".to_string(),
kind: TalkKind::TouraineTech,
audience: Some("Tours".to_string()),
};
println!("Talk info: {}", talk);
Essayons d’afficher simplement les
infos de notre struct.
30. —
03 Traits
Les notions indispensables
Rust sans (trop) roter du sang
let talk = Talk {
title: "Rust sans (trop) roter du sang".to_string(),
kind: TalkKind::TouraineTech,
audience: Some("Tours".to_string()),
};
println!("Talk info: {}", talk);
Uh ?!
31. —
03 Traits
Les notions indispensables
Rust sans (trop) roter du sang
let talk = Talk {
title: "Rust sans (trop) roter du sang".to_string(),
kind: TalkKind::TouraineTech,
audience: Some("Tours".to_string()),
};
println!("Talk info: {}", talk);
Contrat d’un comportement.
Parfois appelé interface dans
d’autres langages.
32. —
03 Traits
Les notions indispensables
Rust sans (trop) roter du sang
use std::fmt::{Display, Formatter};
let talk = Talk {
title: "Rust sans (trop) roter du sang".to_string(),
kind: TalkKind::TouraineTech,
audience: Some("Tours".to_string()),
};
impl Display for Talk {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.title)
}
}
println!("Talk info: {}", talk);
Un bloc impl par implémentation
de trait. Possible d’implémenter
plusieurs traits pour un même
type.
33. —
03 Traits
Les notions indispensables
Rust sans (trop) roter du sang
use std::fmt::{Display, Formatter};
let talk = Talk {
title: "Rust sans (trop) roter du sang".to_string(),
kind: TalkKind::TouraineTech,
audience: Some("Tours".to_string()),
};
impl Display for Talk {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.title)
}
}
println!("Talk info: {}", talk);
Un bloc impl par implémentation
de trait. Possible d’implémenter
plusieurs traits pour un même
type.
34. —
03 Derive
Les notions indispensables
Rust sans (trop) roter du sang
let talk = Talk {
title: "Rust sans (trop) roter du sang".to_string(),
kind: TalkKind::TouraineTech,
audience: Some("Tours".to_string()),
};
println!("Talk info: {:?}", talk);
Revenons à notre erreur…
Il y avait une deuxième option !
35. —
03 Derive
Les notions indispensables
Rust sans (trop) roter du sang
let talk = Talk {
title: "Rust sans (trop) roter du sang".to_string(),
kind: TalkKind::TouraineTech,
audience: Some("Tours".to_string()),
};
println!("Talk info: {:?}", talk);
Revenons à notre erreur…
Il y avait une deuxième option !
36. —
03 Derive
Les notions indispensables
Rust sans (trop) roter du sang
let talk = Talk {
title: "Rust sans (trop) roter du sang".to_string(),
kind: TalkKind::TouraineTech,
audience: Some("Tours".to_string()),
};
println!("Talk info: {:?}", talk);
Non, toujours pas…
37. —
03 Derive
Les notions indispensables
Rust sans (trop) roter du sang
struct Talk {
title: String,
kind: TalkKind,
audience: Option<String>,
}
let talk = Talk {
title: "Rust sans (trop) roter du sang".to_string(),
kind: TalkKind::TouraineTech,
audience: Some("Tours".to_string()),
};
println!("Talk info: {:?}", talk);
Non, toujours pas…
38. —
03 Derive
Les notions indispensables
Rust sans (trop) roter du sang
#[derive(Debug)]
struct Talk {
title: String,
kind: TalkKind,
audience: Option<String>,
}
let talk = Talk {
title: "Rust sans (trop) roter du sang".to_string(),
kind: TalkKind::TouraineTech,
audience: Some("Tours".to_string()),
};
println!("Talk info: {:?}", talk);
Implémentation automatique de
certains traits !
39. —
03 Résumé
Les notions indispensables
Rust sans (trop) roter du sang
#[derive(Debug)]
enum TalkKind {
Conference,
Meetup,
TouraineTech,
}
#[derive(Debug)]
struct Talk {
title: String,
kind: TalkKind,
audience: Option<String>,
}
let talk = Talk {
title: "Rust sans (trop) roter du sang".to_string(),
kind: TalkKind::TouraineTech,
audience: Some("Tours".to_string()),
};
match talk.audience {
Some(audience) => {
println!("Yay! Thanks {}!", audience);
},
None => { println!("Too bad :(") },
}
let cool_lang = talk.title.find("Rust")?;
● Pattern Matching
● Type Result
○ … et ?
● Type Option
○ … et .unwrap()
● Enums
● Structs
● Traits
● Derive
41. —
04 En direct !
Mise en œuvre
Rust sans (trop) roter du sang
☁
42. —
04 En direct !
Mise en œuvre
Rust sans (trop) roter du sang
43. —
04 En direct !
Mise en œuvre
Rust sans (trop) roter du sang
44. —
04 En direct !
Mise en œuvre
Rust sans (trop) roter du sang
45. —
Rust sans (trop) roter du sang
04 Live démo
Mise en œuvre
Live coding de 14min !
Désolé, vous devrez attendre la diffusion
de l’enregistrement vidéo pour en profiter.
Stay tuned :
https://touraine.tech/
https://twitter.com/tourainetech
47. —
Rust sans (trop) roter du sang
05 Plein d’outils !
Les amis de Ferris
reqwest
On a toujours besoin d’un
client HTTP près de soi
clap
Bijou de construction
de CLI
clippy
Encore mieux que le linter
de base de Ferris
serde
Couteau suisse de la
(de)serialization
tokio
anyhow
warp
log
…
49. —
06 HTTP headers
Conclusion
Rust sans (trop) roter du sang
use std::collections ::HashMap;
use reqwest::{blocking ::Response , Error};
use serde::Deserialize ;
#[derive(Deserialize , Debug)]
struct Headers {
headers: HashMap<String, String>,
}
fn main() {
let url: &str = "https://httpbin.org/headers" ;
let http_response : Result<Response , Error> = reqwest::blocking ::get(url);
match http_response {
Ok(response ) => {
let headers = response .json::<Headers>().unwrap();
println! ("Headers: {:?}", headers);
},
Err(_) => {println! ("Failed to call httpbin" );},
}
}
Normalement, vous devriez
désormais être à même de
comprendre globalement ce que
notre premier morceau de code fait
et comment il le fait.
Si vous êtes joueur ou joueuse,
trouvez pourquoi nous mentionnions
“à une exception près” quant à la
sûreté de ce programme !
50. —
Conclusion
Pourquoi Rust maintenant
Rust sans (trop) roter du sang
06
● La Rust Foundation
● 6 ans de “Most Loved Language” StackOverflow…
● L’engouement… que ça soit par les développeurs ou les entreprises
Source: Google Trends, 2010-2023, Mondialement
51. —
Bonus
Pour vous accompagner dans le voyage
Rust sans (trop) roter du sang
06
https://doc.rust-lang.org/stable/book
● https://github.com/rust-lang/rustlings
● https://www.arewewebyet.org
● https://www.reddit.com/r/rust/
● … et de la pratique !
52. —
Bonus
Les objectifs de Rust
Rust sans (trop) roter du sang
06
Les objectifs de Rust :
● Concevoir et implémenter un langage sûr,
concurrent et orienté pratique
● Rust existe car les autres langages avec le même
niveau d’abstraction et de performance ne sont
pas satisfaisants. En particulier :
○ Trop peu d’attention est donnée à la sûreté
○ Ils ont un faible niveau de support des calculs
en parallèle
○ Il y a un clair manque de commodités
pratiques
○ Ils offrent un contrôle limité sur les
ressources
● Rust existe comme une alternative qui fournit à la
fois du code performant et un niveau
d’abstraction confortable, tout en améliorant les
choses sur les 4 précédents points.
Les non-objectifs de Rust :
● Nous n’employons pas de technologie
particulièrement à la pointe.
● Les techniques éprouvées fonctionnent bien.
● Nous ne plaçons pas l’expressivité, le
minimalisme ou l’élégance avant les autres
objectifs.
● Il s’agit d’objectifs louables, mais secondaires.
● Nous ne visons pas la parité avec C++ ou
n’importe quel autre langage. Rust devrait
couvrir le dénominateur commun.
● Nous ne voulons pas être 100% statique, sûr ou
réflectif. Pas de dogme.
● Nous n’exigeons pas que Rust puisse tourner
sur « toutes les plateformes existantes »,
seulement sur les plus courantes et sans
bidouille.
53. Rust sans (trop) roter du sang
Merci ! Derniers mots…
Votre avis compte pour nous !
Alexis « Horgix » Chotard
SRE chez
@Horgix
Horgix
Édouard Siha
Cloud-Native Dev chez
https://blog.wescale.fr/
Il fallait être là
en live :)
54. — Crédits images
Rust sans (trop) roter du sang
99
Colophon
● Logo Rust : Rust Foundation
https://foundation.rust-lang.org/
● Ferris, la mascotte non officielle : Karen Rustad Tölva
https://rustacean.net/
● Logo Cargo : Rust Foundation
https://foundation.rust-lang.org/
● Puces mémoire : Laura Ockel
https://unsplash.com/photos/qOx9KsvpqcM
● Logo C : Domaine public
https://commons.wikimedia.org/wiki/File:C_Programming_Language.svg
● Logo Java : Oracle
https://upload.wikimedia.org/wikipedia/fr/thumb/2/2e/Java_Logo.svg/550px-Java_Logo.svg.png
● Clavier : Sergi Kabrera
https://unsplash.com/photos/2xU7rYxsTiM