Skip to content
Snippets Groups Projects
essential_rust.tex 46.3 KiB
Newer Older
v-lafeychine's avatar
v-lafeychine committed
\documentclass[11pt]{article}

\usepackage[xetex, scale=0.8]{geometry}
\usepackage{enumitem}

\usepackage{hyperref}
Vanille-N's avatar
Vanille-N committed
\usepackage{cleveref}
v-lafeychine's avatar
v-lafeychine committed

\usepackage{titlesec}
\newcommand{\sectionbreak}{\clearpage}

\renewcommand*\contentsname{Table des matières}
\setlength{\parindent}{0pt}

\usepackage{fancyhdr}
\pagestyle{fancy}
\fancyhead[L, R]{}
\fancyhead[C]{L'essentiel du langage Rust (\textit{Ébauche})}
\fancyfoot[C]{Vincent Lafeychine}
\renewcommand{\headrulewidth}{1pt}
\renewcommand{\footrulewidth}{.2pt}
\setlength{\headheight}{15pt}

\usepackage{listings, listings-rust}

\lstdefinestyle{Rust}{
  style=boxed,
  basicstyle={\footnotesize\ttfamily},
  columns=fullflexible,
  texcl
}

\begin{document}

\tableofcontents

Vanille-N's avatar
Vanille-N committed
\section{Introduction : le langage Rust}
v-lafeychine's avatar
v-lafeychine committed

Rust est un langage de programmation système, compilé et multi-paradigme.
\begin{itemize}
Vanille-N's avatar
Vanille-N committed
    \item Langage compilé : le code source n'est pas exécutable tel quel, il faut d'abord le
        transformer avec un compilateur.
    \item Langage de programmation système : très peu de fonctionnalités sont implémentés dans
        les binaires résultants du langage.
        en général il ne se passe rien qui n'ait été explicitement demandé dans le code source.
        Par exemple, il n'y a pas de ramasse-miettes ou d'allocateur automatique.
    \item Langage multi-paradigme : le langage prend beaucoup de concepts des langages
        concurrents, orientés objets et fonctionnel.\\
        Par exemple :
        \begin{itemize}
            \item tout comme dans les langages orientés objets, la plupart du code se fait
                en définissant des structures de données et des méthodes qui opèrent dessus.
            \item tout comme dans les langages fonctionnels, tout est une expression
                (voir \Cref{expressionsEverywhere})
        \end{itemize}
v-lafeychine's avatar
v-lafeychine committed
\end{itemize}


Vanille-N's avatar
Vanille-N committed
L'objectif du Rust est de ne sacrifier aucun des trois critères :
rapidité, sécurité, et concurrence.
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
Pour ce faire, la syntaxe de Rust est enrichie pour permettre au compilateur
d'avoir l'ensemble des informations nécessaires : il y aura souvent plus d'indications de types
à écrire qu'en OCaml et plus d'opérations à rendre explicite (conversions de type,
mutabilité, durées de vies) qu'en C/C++.
v-lafeychine's avatar
v-lafeychine committed


\section{Environnement et outils}

Vanille-N's avatar
Vanille-N committed
L'environnement de Rust se compose de beaucoup d'outils aidant le développement ainsi
que le déploiement.
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
\subsection{\texttt{rustc} : compilateur Rust}
v-lafeychine's avatar
v-lafeychine committed

Le compilateur Rust fonctionne principalement comme les autres compilateurs.
Vanille-N's avatar
Vanille-N committed
Cependant, il est rare de l'utiliser directement car \texttt{cargo} permet
souvent de s'affranchir d'invoquer explicitement \texttt{rustc}.

Voici un exemple de compilation d'un fichier nommé \texttt{source.rs} vers
un binaire exécutable nommé \texttt{bin} en activant les optimisations :
\texttt{rustc source.rs -o bin -O}
Vanille-N's avatar
Vanille-N committed
\subsection{\texttt{cargo} : outil de gestion de projet}
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
\texttt{cargo} est un outil de build, comme peut l'être \texttt{make}, mais comporte
beaucoup plus de fonctionnalités :
v-lafeychine's avatar
v-lafeychine committed
\begin{itemize}
Vanille-N's avatar
Vanille-N committed
  \item téléchargement automatique des dépendances externes
  \item compilation pour le développement et pour la production
      (le premier est plus rapide à compiler mais est moins rapide à l'exécution)
  \item création de la documentation
  \item lancement des tests
  \item appel de commandes externes
v-lafeychine's avatar
v-lafeychine committed
\end{itemize}


Vanille-N's avatar
Vanille-N committed
\subsection{\texttt{clippy} : linter Rust}
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
Le linter lit le code et recherche des patterns qui sont...
\begin{itemize}
    \item ...souvent des indices d'un bug\\
        (variable inutilisée, conversion de type inutile, code inaccessible, ...)
    \item ...considérés comme de mauvaises pratiques\\
        (trop de variables à une lettre, ...)
    \item ...connus pour être moins performants qu'une alternative\\
        (copies profondes inutiles, pointeurs inutiles, structures de données peu
        efficaces en mémoire, ...)
\end{itemize}

On peut lancer un linter basique avec \texttt{cargo check}, et \texttt{cargo clippy} en
est la version plus poussée.
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
On trouve à l'addresse \href{https://rust-lang.github.io/rust-clippy/master/index.html}{https://rust-lang.github.io/rust-clippy/master/index.html}
une liste exhaustive des vérifications de \texttt{clippy} (pas toutes activées par défaut),
avec des explications de pourquoi le pattern est dangereux ou considéré mauvais.
Vanille-N's avatar
Vanille-N committed
\subsection{\texttt{rls} : Language Server Protocol}
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
L'ensemble des IDE migrent vers le protocole Open-Source de Microsoft : Language Server Protocol.
v-lafeychine's avatar
v-lafeychine committed

Ce protocole permet d'avoir les fonctionnalités principales d'un IDE (auto-complétion, saut vers les définitions...) par le biais d'un serveur propre au langage.


Vanille-N's avatar
Vanille-N committed
\subsection{\texttt{rustfmt} : formatage du code}
v-lafeychine's avatar
v-lafeychine committed

Formatage du code afin que tout le monde puisse lire, écrire, et maintenir aisément du code.

Vanille-N's avatar
Vanille-N committed
Il est la plupart du temps invoqué par \texttt{cargo fmt}.

v-lafeychine's avatar
v-lafeychine committed

\section{Apprendre le Rust}

Vanille-N's avatar
Vanille-N committed
Il existe plusieurs sources d'informations pour apprendre le Rust :
v-lafeychine's avatar
v-lafeychine committed
\begin{itemize}
Vanille-N's avatar
Vanille-N committed
  \item \href{https://doc.rust-lang.org/book/}{RustBook} : apprendre par la lecture
  \item \href{https://github.com/rust-lang/rustlings/}{Rustlings} : apprendre par la résolution d'exercices
  \item \href{https://doc.rust-lang.org/stable/rust-by-example/}{Rust by example} : apprendre par de nombreux exemples de cas d'utilisations
v-lafeychine's avatar
v-lafeychine committed
\end{itemize}

Vanille-N's avatar
Vanille-N committed
Quelques autres liens utiles :
\begin{itemize}
    \item \href{https://play.rust-lang.org/}{Playground} : pour faire des tests rapides
        dans le navigateur ou regarder l'assembleur généré
    \item \href{https://this-week-in-rust.org/}{This Week in Rust} : newsletter officielle
    \item \href{https://github.com/rust-lang/rust/}{github:rust} : le code source intégral de
        \texttt{rustc} et de la librairie standard
    \item \href{https://crates.io}{Crates} : le dépôt de paquets officiel depuis lequel sont
        téléchargées les dépendances
    \item \href{https://docs.rs/}{Docs} : la documentation officielle de tous les paquets
\end{itemize}
Vanille-N's avatar
Vanille-N committed
\section{Premiers programmes : découverte du Rust}
v-lafeychine's avatar
v-lafeychine committed

\subsection{Hello World!}

\begin{lstlisting}[style=Rust, language=Rust]
Vanille-N's avatar
Vanille-N committed
// Dans le fichier \texttt{hello\_world.rs}
v-lafeychine's avatar
v-lafeychine committed
fn main() {
Vanille-N's avatar
Vanille-N committed
    println!("Hello World");
v-lafeychine's avatar
v-lafeychine committed
}
\end{lstlisting}

Vanille-N's avatar
Vanille-N committed
Pour compiler ce programme on va directement utiliser \texttt{rustc}, avec la commande
shell suivante :

\texttt{rustc hello\_world.rs}

Le binaire généré sera nommé en fonction du fichier si on ne précise pas explicitement
un autre nom avec l'option \texttt{-o BINAIRE}.

On peut ensuite exécuter normalement \texttt{./hello\_world} qui affichera le texte
\texttt{Hello World} (avec un retour à la ligne).
v-lafeychine's avatar
v-lafeychine committed



\subsection{Quelques notions basiques}

Vanille-N's avatar
Vanille-N committed
Comme C, Rust a de nombreux types numériques de différentes tailles. Contrairement à C
les noms sont unifiés : \texttt{i}/\texttt{u}/\texttt{f} pour ``integer'', ``unsigned integer'',
``floating point'', suivi du nombre de bits.
v-lafeychine's avatar
v-lafeychine committed
\begin{itemize}
Vanille-N's avatar
Vanille-N committed
    \item entiers signés : \texttt{i8}, \texttt{i16}, \texttt{i32}, \texttt{i64} et \texttt{i128}.
    \item entiers non signés : \texttt{u8}, \texttt{u16}, \texttt{u32}, \texttt{u64} et
        \texttt{u128}.
    \item flottants : \texttt{f32}, \texttt{f64}.
    \item entiers de la taille de la machine : \texttt{isize} et \texttt{usize}.\\
        (sont égaux à des \texttt{i32}/\texttt{u32} sur système 32 bits, et
        \texttt{i64}/\texttt{u64} sur système 64 bits)
v-lafeychine's avatar
v-lafeychine committed
\end{itemize}

\begin{lstlisting}[style=Rust, language=Rust]
fn main() {
Vanille-N's avatar
Vanille-N committed
    // Par défaut, les variables sont non-modifiables (tout comme un \texttt{let} en OCaml).
    let x: i8 = 1;  // x vaut 1
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
    // \texttt{let} peut redéfinir une variable qui écrase la précédente
    let x = x + 1; // x vaut 2, le type est inféré à partir de l'expression
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
    {
        // une variable est visible uniquement à l'intérieur du bloc qui la définit
        let x = 5; // x vaut 5
        let y = 4; // y vaut 4
    }
    // x vaut de nouveau 2
    // y n'existe plus
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
    // pour mettre à jour une variable d'une manière visible en dehors du bloc,
    // il faut la déclarer \texttt{mut} (mutable)
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
    // Le mot clé \textbf{mut} permet de rendre modifiable une variable.
    let mut z: i32 = 1;  // similaire à \texttt{let z = ref 1} en OCaml
    z = 4;
    // \textit{Note :} ici le linter génère un avertissement que z n'a pas besoin
    // d'être mutable et que sa première valeur 1 est inutile.

    // z vaut 4
    {
        z += 1;
    }
    // z vaut 5

    // Appel de fonction
    // (la fonction peut être définie plus tard dans le fichier,
    // il n'est pas nécessaire de la déclarer)
    let result = add(x, y);  // ici aussi le type est deviné grâce à la signature de add

    // \textit{Note :} dans ce document on explicitera les types plus que nécessaire
    // la plupart des fonctions en Rust peuvent s'écrire avec pour unique annotation
    // de type la signature de la fonction.
v-lafeychine's avatar
v-lafeychine committed

    println!("Result: {}", result);   // "Result: 5"
}

// La fonction prend deux arguments qui sont des \textbf{i32} et retourne un \textbf{i32}.
fn add(x: i32, y: i32) -> i32 {
Vanille-N's avatar
Vanille-N committed
    x + y  // pas besoin de mot-clé return, la dernière expression est renvoyée
v-lafeychine's avatar
v-lafeychine committed
}
\end{lstlisting}


\newpage


Vanille-N's avatar
Vanille-N committed
\subsection{Chaînes de caractères (introduction)}
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
Pour une explication plus poussée des différents types de chaînes de caractères,
voir \Cref{advancedStrings}.

Les chaînes de caractères en Rust sont toutes encodés en \texttt{UTF-8}, et
prennent principalement l'un des deux formats suivants :
v-lafeychine's avatar
v-lafeychine committed
\begin{itemize}
Vanille-N's avatar
Vanille-N committed
  \item chaîne de caractères allouée dynamiquement (type \texttt{String}).
  \item référence non modifiable, nommée slice (type \texttt{\&str}).
v-lafeychine's avatar
v-lafeychine committed
\end{itemize}

\begin{lstlisting}[style=Rust, language=Rust]
Vanille-N's avatar
Vanille-N committed
fn main() {
    // Une chaîne entre "" est une \&str
v-lafeychine's avatar
v-lafeychine committed
    let hello_world: &str = "Hello World";

Vanille-N's avatar
Vanille-N committed
    // On peut créer une String à partir d'une \&str
    let hello_world_string: String = hello_world.to_string();
    // alternative équivalente : String::from(hello\_world)

    // Réciproquement on peut convertir une String en \&str
    // soit tout entière :
    let hello_world_slice: &str = &hello_world_string;
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
    // soit partiellement, ici seulement les caractères 6 à 10 inclus.
    let world_slice: &str = &hello_world_string[6..11];
v-lafeychine's avatar
v-lafeychine committed

    println!("Hi {}", world_slice);   // "Hi World"
}
\end{lstlisting}

Vanille-N's avatar
Vanille-N committed
Il y a des choses qu'on peut ou ne peut pas faire avec ces deux formats :
\begin{itemize}
    \item on ne peut pas modifier une \texttt{\&str}, alors que \texttt{String}
        a des méthodes pour être modifiée
    \item on ne peut pas additionner (concaténer) deux \texttt{String} alors qu'on peut
        additionner \texttt{\&str + \&str} ou encore \texttt{String + \&str}
\end{itemize}
Pour beaucoup de raisons il y a dans la librairie standard des fonctions qui demandent
l'un ou l'autre type. Heureusement la conversion est facile.


On verra souvent des erreurs de compilation comme celle-ci :
\begin{lstlisting}[style=Rust]
   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
 --> src/main.rs:4:14
  |
4 |     hello += world;
  |              ^^^^^
  |              |
  |              expected `&str`, found struct `String`
  |              help: consider borrowing here: `&world`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to previous error
\end{lstlisting}
Il suffit de remplacer la ligne en \texttt{hello += \&world;} pour que cela fonctionne.
\newpage

\begin{lstlisting}[style=Rust, language=Rust]
fn main() {
    let mut hello = String::new();  // nouvelle String vide

    hello.push_str("Hello ");  // concatène du texte à hello
    hello += "World";  // += est équivalent à push\_str
    hello.push('!');  // concatène un unique caractère
                      // ('' est la notation pour le type \texttt{char})
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
    println!("{}", hello);  // "Hello World!"
}
\end{lstlisting}
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
\subsection{Formattage}
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
La manière standard de formatter du texte est la famille de macros \texttt{format!}.
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
(pour plus de détails sur les macros en général, voir \Cref{advancedMacros})
v-lafeychine's avatar
v-lafeychine committed

\begin{lstlisting}[style=Rust, language=Rust]
fn main() {
Vanille-N's avatar
Vanille-N committed
    print!("{} {} {} ", 1, '2', "trois");  // affiche "1 2 trois" sans retour à la ligne
    println("quatre {} {}", 5, 6.0);  // affiche "quatre 5 6" avec un retour à la ligne

    eprint!("erreur ");
    eprintln!("grave");  // pareil mais sur la sortie stderr

    let formatteur: std::fmt::Formatter = ...;  // comment obtenir un formatteur est au delà 
                                                // des objectifs de cette section, on montre
                                                // juste qu'il s'utilise de manière similaire
    write!(formatteur, "truc");
    writeln!(formatteur, "bidule");

    let s: String = format!("{} + {} = {}", 1, 2, 3);
    // s vaut "1 + 2 = 3"

    // À chaque fois on peut préciser le format d'affichage dans les \{\}
    let hello = "hello";
    println!("{hello}");  // équivalent de println!("{}", hello)
    println!("{hello:#?}");  // affichage de debug, écrit "hello" en incluant les guillemets
    println!("{hello: >10}");  // aligne à une largeur de 10 en rajoutant des espaces
    // et beaucoup d'autres, parmi lesquels :
    // - comment afficher le signe + ou -
    // - comment représenter les flottants
    // - etc...
}
\end{lstlisting}
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
\section{Tableaux}
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
Comme pour les chaînes de caractères, les tableaux se présentent sous plusieurs formes.
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
Tous peuvent être modifié avec \texttt{tab[i] = v;} (à condition bien sûr qu'ils
soient déclarés \texttt{mut}).
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
\begin{itemize}
    \item \texttt{Vec<Type>} : un tableau dynamique qui contient des éléments de type \texttt{T}.\\
        Peut être redimensionné avec \texttt{push}.\\
        Equivalent de \texttt{String} : un \texttt{String} est presque un
        \texttt{Vec<char>} avec quelques méthodes en plus.\\~\\
        D'ailleurs pour convertir de l'un à l'autre on dispose de\\
        \texttt{let chaine: String = vecteur.iter().collect();}\\
        \texttt{let vecteur: Vec<char> = chaine.chars().collect();}
    \item \texttt{[Type; Taille]} (e.g. \texttt{[u64; 5]}) : un tableau de taille fixée connue.
    \item \texttt{\&[Type]} : un tableau de taille fixée inconnue.\\
        Équivalent de \texttt{\&str}.
\end{itemize}
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
Ici encore on peut dans une certaine mesure convertir d'un type à un autre.
v-lafeychine's avatar
v-lafeychine committed

\begin{lstlisting}[style=Rust, language=Rust]
fn main() {
Vanille-N's avatar
Vanille-N committed
    let vector: Vec<usize> = vec![1, 2, 3];
    let array: [usize; 3] = [1, 2, 3];
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
    let slice_of_full_vector: &[usize] = &vector;
    let slice_of_partial_vector: &[usize] = &vector[..2];
    let slice_of_full_array: &[usize] = &array;
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
    let vector_of_array = array.to_vec();
    let vector_of_slice = &array[1..].to_vec();
v-lafeychine's avatar
v-lafeychine committed
}
\end{lstlisting}


Vanille-N's avatar
Vanille-N committed
\section{Références}

Les références remplacent la plupart du temps la notion de pointeur.

En plus de pointer vers une donnée parfois modifiable, elles évitent des copies
inutiles et peuvent garantir qu'il n'y a pas plusieurs personnes qui ont un droit
d'écriture en même temps.
v-lafeychine's avatar
v-lafeychine committed

\begin{lstlisting}[style=Rust, language=Rust]
fn main() {
Vanille-N's avatar
Vanille-N committed
    let dix: usize = 10;
    let dix_ref: &usize = &dix;  // dix\_ref pointe vers dix
    println("{}", *dix_ref);  // 10

    let mut un: usize = 0;  // un est modifiable
    {
        let un_mut_ref: &mut usize = &mut un;  // référence avec droit d'écriture sur un
                                               // un ne peut pas être lu ni écrit pendant
                                               // que un\_mut\_ref existe
        *un_mut_ref = 1;  // modifie la valeur de un

        // un += 1;  // erreur
        // let x = un;  // erreur
    } // un\_mut\_ref cesse d'exister, on peut de nouveau lire et écrire dans un

    {
        let un_ref: &usize = &un;  // référence avec droit de lecture seulement
                                   // un peut être lu mais pas écrit pendant que
                                   // un\_ref existe
        // un += 1;  // erreur
        // let x = un;  // ok
    } // un\_ref cesse d'exister, on peut de nouveau écrire dans un


    // Les références sont surtout utiles pour appeler des fonctions
    let mut x: usize = 1;
    incrementer(&mut x);  // permet de donner un droit d'écriture sur une variable locale
    println!("La nouvelle valeur de x est {}", x);  // 2

    let v: Vec<usize> = vec![1, 2, 3, 4, 5];
    let s = somme(&v);  // droit de lecture uniquement, permet de ne pas avoir à copier v
    println!("La somme de {:#?} est {}", s);  // 15
    // On a la garantie que v a toujours exactement le même contenu qu'avant l'appel à la fonction
}

fn incrementer(ptr: &mut usize) {
    *ptr += 1;
}
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
fn somme(vect: &Vec<usize>) -> usize {
    vect.iter().sum()
v-lafeychine's avatar
v-lafeychine committed
}
\end{lstlisting}

Vanille-N's avatar
Vanille-N committed
La forme complète d'une référence est \texttt{\&'lft Type} ou \texttt{\&'lft mut Type} :
voir \Cref{advancedLifetimes} pour plus de détails.
Vanille-N's avatar
Vanille-N committed

v-lafeychine's avatar
v-lafeychine committed
\section{Conditions et boucles}

\begin{lstlisting}[style=Rust, language=Rust]
fn main() {
    let age: u32 = 15;
Vanille-N's avatar
Vanille-N committed

v-lafeychine's avatar
v-lafeychine committed
    if age < 18 {
        println!("Tu es mineur(e)");
    } else {
        println!("Tu es majeur(e)");
    }   // "Tu es mineur(e)"
}
\end{lstlisting}

\begin{lstlisting}[style=Rust, language=Rust]
fn main() {
Vanille-N's avatar
Vanille-N committed
    let array: [char; 4] = ['a', 'b', 'c', 'd'];
v-lafeychine's avatar
v-lafeychine committed

    // On va itérer sur chacun des éléments du tableau
Vanille-N's avatar
Vanille-N committed

    for elem in array.iter() {  // équivalent: for elem in \&array
        print!("{} ", elem);
    }  // affiche "a b c d "

    for (index, elem) in array.iter().enumerate() {
        print!("{}:{} ", index, elem);
    }  // affiche "0:a 1:b 2:c 3:d "

    for index in 0..array.len() {
        print!("{}:{} ", index, array[elem]);
    }  // idem

    for elem in array.into_iter() {  // équivalent: for elem in array
        print!("{} ", elem);
    } // cette boucle semble très similaire à la première,
      // mais il y a deux différences :
      // - elem est de type char au lieu de \&char
      // - array n'est plus utilisable ensuite

v-lafeychine's avatar
v-lafeychine committed

    // On va modifier (de manière impérative) chaque élément du tableau
    let mut vector: Vec<i32> = vec![10, 20, 30];
Vanille-N's avatar
Vanille-N committed
    for elem in vector.iter_mut() {  // équivalent: for elem in \&mut vector
v-lafeychine's avatar
v-lafeychine committed
        *elem += 50;
    }

    println!("{:?}", vector);   // "[60, 70, 80]"
}
\end{lstlisting}

\begin{lstlisting}[style=Rust, language=Rust]
fn main() {
    let mut age = 15;

    while age < 18 {
        println!("Tu es encore trop jeune.");
        age += 1;
    }

    println!("Tu as bien l'âge requis maintenant.");
}
\end{lstlisting}

\begin{lstlisting}[style=Rust, language=Rust]
fn main() {
    // La syntaxe \textbf{loop} est équivalente à \textbf{while true}
    loop {
        println!("Hello!");

        // Keyword \textbf{break}, comme en C
        break;
    }
}
\end{lstlisting}

Vanille-N's avatar
Vanille-N committed
Il se trouve que les boucles et tests sont des expressions: voir \Cref{expressionsEverywhere}.
v-lafeychine's avatar
v-lafeychine committed


\section{Structures, Enums et Pattern matching}

Vanille-N's avatar
Vanille-N committed
\subsection{Types produits}
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
Il y a trois formes pour déclarer un type qui est la concaténation de
valeurs de plusieurs types.
Tous peuvent être déstructurés similairement.
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
Ici on veut que le type \texttt{T} ait trois valeurs de types \texttt{u8}, \texttt{char},
\texttt{String}.

Tuples:
v-lafeychine's avatar
v-lafeychine committed
\begin{lstlisting}[style=Rust, language=Rust]
Vanille-N's avatar
Vanille-N committed
type T = (u8, char, String);

fn main() {
    let t = (1, 'a', "un".to_string());
    let (n, c, s) = t;
    let n = t.0;
v-lafeychine's avatar
v-lafeychine committed
}
Vanille-N's avatar
Vanille-N committed
// avantages: peut être créé à la volée
// inconvénients: long à écrire si il y a beaucoup de valeurs, on risque d'oublier des valeurs
// utilisation: pour des types qui n'ont pas particulièrement de signification dans le programme
\end{lstlisting}
~\\

Tuple struct:
\begin{lstlisting}[style=Rust, language=Rust]
struct T(u8, char, String);
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
fn main() {
    let t = T(1, 'a', "un".to_string());
    let T(n, c, s) = t;
    let n = t.0;
}
// avantages: ne peut pas être confondu avec d'autres types qui ont les mêmes éléments
// inconvénients: on peut oublier l'ordre des éléments
// utilisation: pour des types qui contiennent peu de valeurs
\end{lstlisting}
Voir \Cref{patternNewtype} pour une utilisation fréquente de cette forme
~\\

Struct:
\begin{lstlisting}[style=Rust, language=Rust]
struct T {
    num: u8,
    chr: char,
    str: String,
v-lafeychine's avatar
v-lafeychine committed
}

fn main() {
Vanille-N's avatar
Vanille-N committed
    let t = T {
        num: 1,
        chr: 'a',
        str: "un".to_string(),
v-lafeychine's avatar
v-lafeychine committed
    };
Vanille-N's avatar
Vanille-N committed
    let T { num: n, chr: c, str: s } = t;
    let n = t.num;
v-lafeychine's avatar
v-lafeychine committed
}
Vanille-N's avatar
Vanille-N committed
// avantages: on ne risque pas d'oublier la signification de chaque champ
// inconvénients: long à écrire
// utilisation: pour des types qui ont soit beaucoup de valeurs,
//              soit plusieurs valeurs qu'on risque de confondre
\end{lstlisting}
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
\subsection{Type somme}

Pour un type qui est l'union de plusieurs types, on utilise une \texttt{enum}.

Les variants peuvent eux-mêmes être des types produits.

\begin{lstlisting}[style=Rust, language=Rust]
enum U {
    Zero,
    Un(u8),
    Deux(u8, char),
    Trois {
        n: u8,
        c: char,
        f: f64,
    },
}

fn main() {
    let u0 = U::Zero;
    let u1 = U::Un(1);
    let u2 = U::Deux(2, 'b');
    let u3 = U::Trois {
        n: 3,
        c: 'c',
        f: 3.0,
    };

    match u3 with {
        U::Zero => println!("zero"),
        U::Un(n) => println!("un:{}", n),
        U::Deux(n, c) => println!("deux:{},{}", n, c),
        U::Trois { n, c, f } => println!("trois:{},{},{}", n, c, f),
v-lafeychine's avatar
v-lafeychine committed
\end{lstlisting}

\newpage

L'exemple précédent fonctionne correctement, cependant, l'enum représentant la possibilité devrait être dupliquée afin d'être compatible avec d'autres types.

Vanille-N's avatar
Vanille-N committed
On peut utiliser les types génériques pour résoudre ce problème :
v-lafeychine's avatar
v-lafeychine committed

\begin{lstlisting}[style=Rust, language=Rust]
Vanille-N's avatar
Vanille-N committed
enum Maybe<T> {     // L'enum \textbf{Maybe<T>} se compose :
    Just(T),        //  - soit d'un élément, qui est de type T.
    Nothing,        //  - soit d'aucun élément.
v-lafeychine's avatar
v-lafeychine committed
}

struct User {
    id: i32,
    name: String,
    nickname: Maybe<String>,   // Utilisation de l'enum Maybe avec comme type String
}

fn main() {
    let user_one: User = User {
        id: 1,
        name: "Anthony".to_string(),
        nickname: Maybe::Just("Antho".to_string()),
    };
    
    let user_two: User = User {
        id: 2,
        name: "Luc".to_string(),
        nickname: Maybe::Nothing,
    };
   
    print_user(user_one);   // "1: Anthony aka Antho"
    print_user(user_two);   // "2: Luc"
}

fn print_user(user: User) {
    match user.nickname {
        Maybe::Just(nickname) => println!("{}: {} aka {}", user.id, user.name, nickname),
        Maybe::Nothing => println!("{}: {}", user.id, user.name),
    }
}
\end{lstlisting}

\vspace{.5cm}

Vanille-N's avatar
Vanille-N committed
L'enum \texttt{Maybe<T>} est présente en Rust sous cette forme :
v-lafeychine's avatar
v-lafeychine committed

\begin{lstlisting}[style=Rust, language=Rust]
enum Option<T> {
    Some(T),
    None,
}
\end{lstlisting}



\section{Méthodes de structures}

Afin de contraindre l'accessibilité à notre donnée, il est possible d'utiliser des méthodes de structures.

Cette fonctionnalité de Rust permet de découper correctement une architecture afin de regrouper les fonctions.

Dans l'exemple, on va ajouter la fonction \texttt{print} sur notre structure \texttt{User}.

\begin{lstlisting}[style=Rust, language=Rust]
struct User {
    id: i32,
    name: String,
    nickname: Option<String>,
}

// On va implémenter des fonctions pour la structure \textbf{User}.
impl User {
    // La fonction \textbf{print} prend la structure en paramètre.
    fn print(&self) {
        match &self.nickname {
            Option::Some(nickname) => println!("{}: {} aka {}", self.id, self.name, nickname),
            Option::None => println!("{}: {}", self.id, self.name),
        }
    }
}

fn main() {
    let user_one: User = User {
        id: 1,
        name: "Anthony".to_string(),
        nickname: Option::Some("Antho".to_string()),
    };

    let user_two: User = User {
        id: 2,
        name: "Luc".to_string(),
        nickname: Option::None,
    };

    user_one.print();   // "1: Anthony aka Antho"
    user_two.print();   // "2: Luc"
}
\end{lstlisting}



\section{Traits}

Les traits sont des ensembles de méthodes que les types sur lesquels ils sont appliqués doivent implémenter.

Par exemple, pour utiliser \texttt{println!}, il faut que le type implémente l'ensemble des méthodes du trait \texttt{fmt::Display} pour pouvoir afficher.

Vanille-N's avatar
Vanille-N committed
Le trait \texttt{fmt::Display} ne contient qu'une seule méthode : \texttt{fmt}.
v-lafeychine's avatar
v-lafeychine committed

\begin{lstlisting}[style=Rust, language=Rust]
use std::fmt;   // Permet d'écrire fmt::xxx au lieu de std::fmt::xxx

struct User {
    id: i32,
    name: String,
    nickname: Option<String>,
}

// Pour utiliser \textbf{{}} dans \textbf{println!}, le trait \textbf{fmt::Display} doit être implémenté.
impl fmt::Display for User {
    // La signature de \textbf{fmt} doit être pareille que celle définie par le trait.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match &self.nickname {
            Option::Some(nickname) => write!(f, "{}: {} aka {}", self.id, self.name, nickname),
            Option::None => write!(f, "{}: {}", self.id, self.name),
        }
    }
}

fn main() {
    let user_one: User = User {
        id: 1,
        name: "Anthony".to_string(),
        nickname: Option::Some("Antho".to_string()),
    };

    let user_two: User = User {
        id: 2,
        name: "Luc".to_string(),
        nickname: Option::None,
    };

    println!("{}", user_one);   // "1: Anthony aka Antho"
    println!("{}", user_two);   // "2: Luc"
}
\end{lstlisting}

Ainsi, l'exemple ci-dessus peut utiliser \texttt{println!} directement.

Tous les types primaires de Rust implémentent le trait \texttt{fmt::Display}.

\newpage

La structure de donnée \texttt{Box} permet de stocker un type d'objet, mais \texttt{Box} permet aussi de contenir un trait de l'objet.

Vanille-N's avatar
Vanille-N committed
Ainsi, le code suivant est réalisable :
v-lafeychine's avatar
v-lafeychine committed

\begin{lstlisting}[style=Rust, language=Rust]
use std::fmt;

// Le keyword \textbf{dyn} permet d'utiliser un trait
fn drawer(vector: Vec<Box<dyn fmt::Display>>) {
    for elem in vector {
        println!("{}", elem);
    }
}

fn main() {
    drawer(vec![
        Box::new("Hello"),        // "Hello"
        Box::new(12),             // "12"
        Box::new(Box::new(24)),   // "24"
    ]);
}
\end{lstlisting}



\section{Gestion des erreurs}

Vanille-N's avatar
Vanille-N committed
Rust a deux manières de gérer les erreurs à l'exécution :
v-lafeychine's avatar
v-lafeychine committed
\begin{itemize}
  \item Erreurs fatales, non-rattrapables, qui va libérer toutes les variables et fermer le programme.
  \item Erreurs rattrapables, dont il faudra gérer l'échec.
\end{itemize}

\begin{lstlisting}[style=Rust, language=Rust]
fn main() {
    let vector = vec![1, 2, 3];

    // Accès à un élément non-présent dans le vecteur.
    v[5];   // Rust va générer une erreur fatale grâce à \textbf{panic!}
}
\end{lstlisting}

Vanille-N's avatar
Vanille-N committed
Pour les erreurs non-fatales, Rust a la structure de donnée suivante :
v-lafeychine's avatar
v-lafeychine committed

\begin{lstlisting}[style=Rust, language=Rust]
enum Result<T, E> {
    Ok(T),
    Err(E),
}
\end{lstlisting}

Vanille-N's avatar
Vanille-N committed
Utilisons cette structure afin de faire un programme pour lire un fichier :
v-lafeychine's avatar
v-lafeychine committed

\begin{lstlisting}[style=Rust, language=Rust]
use std::fs::File;   // Permet d'écrire \textbf{File::open} au lieu de \textbf{std::fs::File::open}
use std::io;         // Permet d'écrire \textbf{io::Error}  au lieu de \textbf{std::io::Error}
use std::io::Read;   // Permet d'utiliser la fonction \textbf{read\_to\_string()}

fn main() {
    match read_file() {
        Ok(contents) => println!("{}", contents),     // Affiche le contenu du fichier.
        Err(e) => eprintln!("Error: {}", e),          // Affiche l'erreur rencontrée.
    }
}

fn read_file() -> Result<String, io::Error> {
    let file = File::open("hello.txt");
    
    let mut file = match file {
Vanille-N's avatar
Vanille-N committed
        Ok(file) => file,            // Le fichier n'a pas eu de problème pour être ouvert :
                                     // on renvoie la structure du fichier.
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
        Err(e) => return Err(e),     // Le fichier a eu un problème pour être ouvert :
                                     // on renvoie l'erreur.
v-lafeychine's avatar
v-lafeychine committed
    };
    
    let mut s = String::new();
 
    return match file.read_to_string(&mut s) {
Vanille-N's avatar
Vanille-N committed
        Ok(_) => Ok(s),      // Le fichier n'a pas eu de problème pour être lu :
                             // on renvoie le contenu du fichier.
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
        Err(e) => Err(e),    // Le fichier a eu un problème pour être lu :
                             // on renvoie l'erreur.
v-lafeychine's avatar
v-lafeychine committed
    };
}
\end{lstlisting}



\section{Appartenance (\textit{Ownership})}

Tous les programmes doivent gérer la façon dont ils utilisent la mémoire.
\begin{itemize}
  \item Certains langages, comme Java ou Go, utilisent un ramasse-miette détectant la mémoire qui n'est plus utilisée.
  \item D'autres langages, comme le C, requiert que le développeur explicite l'allocation et la désallocation de la mémoire.
\end{itemize}

Vanille-N's avatar
Vanille-N committed
Rust utilise une autre approche : la mémoire est gérée par un système d'appartenance que le compilateur va vérifier à la compilation.
v-lafeychine's avatar
v-lafeychine committed

Vanille-N's avatar
Vanille-N committed
Le système d'appartenance a trois règles :
v-lafeychine's avatar
v-lafeychine committed
\begin{itemize}
Vanille-N's avatar
Vanille-N committed
  \item chaque valeur en Rust a une variable correspondante, appelée le propriétaire (owner).
  \item il ne peut y avoir qu'un seul propriétaire à la fois.
  \item quand le propriétaire sort du scope, la valeur va être libérée.
v-lafeychine's avatar
v-lafeychine committed
\end{itemize}

\begin{lstlisting}[style=Rust, language=Rust]
fn main() {
    {
        // \textbf{s} est une chaîne de caractères explicitement alloué dans le tas
        let s = String::from("Hello world");

        // Utilisation de \textbf{s}
        println!("{}", s);   // "Hello world"

    }   // \textbf{s} est libéré

    // \textbf{s} n'est plus valide: Il est impossible de l'utiliser
}
\end{lstlisting}

La libération se fait grâce à l'appel de \texttt{drop()}, cette fonction sur la structure \texttt{String()} permet de libérer la mémoire.

\newpage

Vanille-N's avatar
Vanille-N committed
Concernant l'appartenance, le déplacement se comporte de deux façons :
v-lafeychine's avatar
v-lafeychine committed
\begin{itemize}
Vanille-N's avatar
Vanille-N committed
  \item Si la valeur est uniquement dans la \texttt{pile} : un \textit{deep copy} sera effectué, cela permet de copier l'ensemble de la structure dans la nouvelle variable.
  \item Si la valeur utilise le \texttt{tas} : un \textit{shallow copy} sera effectué, la variable déplacée devient invalide.
v-lafeychine's avatar
v-lafeychine committed
\end{itemize}

\begin{lstlisting}[style=Rust, language=Rust]
fn main() {
Vanille-N's avatar
Vanille-N committed
    // Deep copy : \textbf{var2} va copier la valeur de \textbf{var1}
v-lafeychine's avatar
v-lafeychine committed
    let var1 = 12;
    let var2 = var1;

    println!("{}", var1);   // "12"
}
\end{lstlisting}

\begin{lstlisting}[style=Rust, language=Rust]
fn main() {
Vanille-N's avatar
Vanille-N committed
    // Shallow copy : \textbf{var2} va prendre l'appartenance sur \textbf{var1}
v-lafeychine's avatar
v-lafeychine committed
    let var1 = String::from("Hello world");
    let var2 = var1;

    // \textbf{var1} est donc devenue invalide.

Vanille-N's avatar
Vanille-N committed
    println!("{}", var1);   // Erreur de compilation : Utilisation d'une valeur déplacée.
v-lafeychine's avatar
v-lafeychine committed
}
\end{lstlisting}



\section{Modularité}

La modularité d'un langage de programmation permet une hiérarchie séparée en unités logiques capable d'interagir entre-eux.

Un module est un ensemble de fonctions, structures, traits et même d'autres modules.

\begin{lstlisting}[style=Rust, language=Rust]
// Définition du module nommé \textbf{my\_mod}
mod my_mod {

    // Cette fonction n'est disponible que dans cette scope.
    fn private_function() {
        println!("Call private_function()");
    }

    // Le mot clé \textbf{pub} permet de rendre disponible cette fonction.
    pub fn public_function() {
        println!("Call public_function()");
        private_function();
    }

    // Définition du sous-module nommé \textbf{nested}
    pub mod nested {
       pub fn nested_function() {
           println!("Call nested_function()");
       }
    }
    
    pub fn public_call_nested() {
        println!("Call public_call_nested()");

        // Appel de la fonction de \textbf{nested} grâce à l'utilisation de \textbf{::}
        nested::nested_function();
    }
}

fn public_function() {
    println!("Call public_function() that isn't in module");
}

fn main() {
    public_function();                   // "Call public\_function() that isn't in module"

    my_mod::public_function();           // "Call public\_function()"
                                         // "Call private\_function()"

    my_mod::nested::nested_function();   // "Call nested\_function()"
    
    my_mod::public_call_nested();        // "Call public\_call\_nested()"
                                         // "Call nested\_function()"
}
\end{lstlisting}

Le mot clé \texttt{use}, vu dans les exemples précédents, permet de ne pas spécifier l'ensemble du chemin pour accéder à une fonction.

Il est possible de déplacer un module dans un fichier spécifique en respectant l'architecture de \texttt{cargo}.