Skip to content
Snippets Groups Projects
Unverified Commit 76ee4066 authored by Vanille-N's avatar Vanille-N
Browse files

relecture: erreurs

parent 7888d4a7
No related branches found
No related tags found
No related merge requests found
......@@ -771,14 +771,19 @@ impl User {
\section{Traits}
Les traits sont des ensembles de méthodes que les types sur lesquels ils sont appliqués doivent implémenter.
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.
Ils permettent de grouper des objets qui implémentent une fonctionnalité commune,
et certains donnent accès à des fonctionalités spéciales.
Le trait \texttt{fmt::Display} ne contient qu'une seule méthode : \texttt{fmt}.
Par exemple, pour utiliser \texttt{println!}, il faut que le type implémente
l'ensemble des méthodes du trait \texttt{std::fmt::Display} pour pouvoir afficher.
Le trait \texttt{Display} ne contient qu'une seule méthode : \texttt{fmt}.
\begin{lstlisting}[style=Rust, language=Rust]
use std::fmt; // Permet d'écrire fmt::xxx au lieu de std::fmt::xxx
use std::fmt;
struct User {
id: i32,
......@@ -795,51 +800,81 @@ impl fmt::Display for User {
Option::None => write!(f, "{}: {}", self.id, self.name),
}
}
// Grâce à ce trait on peut maintenant écrire
// println!("{}", user\_one);
}
\end{lstlisting}
fn main() {
let user_one: User = User {
id: 1,
name: "Anthony".to_string(),
nickname: Option::Some("Antho".to_string()),
};
Tous les types primaires de Rust implémentent le trait \texttt{fmt::Display}.
let user_two: User = User {
id: 2,
name: "Luc".to_string(),
nickname: Option::None,
};
Voici un aperçu de quelques uns des autres traits les plus fréquents :
\begin{itemize}
\item \texttt{fmt::Debug} pour pouvoir afficher avec le formatteur de
debug \texttt{\{:\#?\}}
\item \texttt{ops::\{Add, Mul, Sub, Div\}} pour pouvoir utiliser les opérateurs
\texttt{+},\texttt{*},\texttt{-},\texttt{/}
\item \texttt{clone::Clone} pour pouvoir dupliquer l'objet
\item \texttt{default::Default} pour les types qui ont une valeur par défaut
\item \texttt{cmp::\{Ord, Eq, PartialEq\}} pour avoir accès aux opérateurs de comparaison
\item \texttt{error::Error} pour les types qui représentent une erreur d'exécution
\item \texttt{string::\{ToString, FromString\}} pour la conversion vers/depuis une
\texttt{String}
\item \texttt{convert::\{Into, From\}} pour les autres conversions de type
\item \texttt{io::\{Read, Write\}} pour lire/écrire dans un fichier
\item \texttt{iter::Iterator} pour pouvoir utiliser dans une boucle \texttt{for}
\item et beaucoup d'autres
\end{itemize}
println!("{}", user_one); // "1: Anthony aka Antho"
println!("{}", user_two); // "2: Luc"
}
\end{lstlisting}
\newpage
Ainsi, l'exemple ci-dessus peut utiliser \texttt{println!} directement.
On peut utiliser des traits pour implémenter une seule fois une fonctionalité
qui ne dépend que de quelques méthodes contenues dans des traits.\\
Tous les types primaires de Rust implémentent le trait \texttt{fmt::Display}.
Par exemple si on sait afficher un type d'objets alors on sait afficher un vecteur de ce type.
\newpage
Cela s'exprime par
\begin{lstlisting}[style=Rust, language=Rust]
use std::fmt;
fn afficher_vec<T>(vec: Vec<T>)
where T: fmt::Display
{
for elem in vec {
println!("{}", elem);
}
}
fn main() {
afficher_vec::<usize>(vec![1, 2, 3]);
afficher_vec::<char>(vec!['a', 'b', 'c']);
afficher_vec::<&str>(vec!["un", "deux", "trois"]);
}
\end{lstlisting}
La performance est exactement la même que si on avait écrit trois fois la fonction
\texttt{afficher\_vec} séparément pour \texttt{T = usize}, \texttt{T = char} et \texttt{T = \&str}.\\
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.
Ainsi, le code suivant est réalisable :
On peut aussi dire qu'une valeur doit pouvoir prendre dynamiquement n'importe quel
type qui implémente un trait (mais cela s'accompagne de pénalités de performance).
Pour cela on écrit
\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 {
// Le mot-clé \textbf{dyn} indique que le type ne sera connu qu'à l'exécution
// Il est nécessaire de mettre l'objet dans un \texttt{Box<...>} car on ne connait
// a priori pas sa taille en mémoire
fn afficher_vec_dyn(vec: Vec<Box<dyn fmt::Display>>) {
for elem in vec {
println!("{}", elem);
}
}
fn main() {
drawer(vec![
Box::new("Hello"), // "Hello"
Box::new(12), // "12"
Box::new(Box::new(24)), // "24"
affiche_vec_dyn(vec![
Box::new("Hello"),
Box::new(12),
Box::new(Box::new(24)),
]);
}
\end{lstlisting}
......@@ -850,34 +885,61 @@ fn main() {
Rust a deux manières de gérer les erreurs à l'exécution :
\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.
\item erreurs fatales, non-rattrapables, qui vont libérer toutes la mémoire
et fermer le programme.
\item erreurs rattrapables, dont il faudra gérer l'échec.
\end{itemize}
Il est bien sûr facile de convertir une erreur rattrapable en erreur non rattrapable,
mais l'inverse est par définition impossible.
\begin{lstlisting}[style=Rust, language=Rust]
fn main() {
let vector = vec![1, 2, 3];
let message = "AAAAAAAAAAAAAA";
panic!("Erreur fatale: {}", message);
}
\end{lstlisting}
// 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!}
\begin{lstlisting}[style=Rust, language=Rust]
fn main() {
let none: Option<usize> = None;
let _ = none.unwrap();
}
\end{lstlisting}
Ce code génère une erreur parce qu'il y a quelque part dans la librairie standard
(\href{https://doc.rust-lang.org/src/core/option.rs.html#743}{ici})
le code suivant :
\begin{lstlisting}[style=Rust, language=Rust]
impl<T> Option<T> {
fn unwrap(self) -> T {
match self {
Some(t) => t,
None => panic!("Impossible d'extraire la valeur de None"),
}
}
}
\end{lstlisting}
Un autre exemple de méthode qui génère ce type d'erreur est l'accès à un
indice trop grand d'un tableau (implémenté par \texttt{index} de
\texttt{std::ops::Index}).\\
\newpage
Pour les erreurs non-fatales, Rust a la structure de donnée suivante :
\begin{lstlisting}[style=Rust, language=Rust]
enum Result<T, E> {
Ok(T),
Err(E),
Ok(T), // résultat de type T
Err(E), // erreur de type E
}
\end{lstlisting}
Utilisons cette structure afin de faire un programme pour lire un fichier :
\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()}
use std::fs::File;
use std::io;
use std::io::Read; // Permet d'utiliser la fonction \textbf{read\_to\_string()} du trait Read
fn main() {
match read_file() {
......@@ -888,24 +950,43 @@ fn main() {
fn read_file() -> Result<String, io::Error> {
let file = File::open("hello.txt");
let mut file = match file {
Ok(file) => file, // Le fichier n'a pas eu de problème pour être ouvert :
// on renvoie la structure du fichier.
Err(e) => return Err(e), // Le fichier a eu un problème pour être ouvert :
// on renvoie l'erreur.
let mut file = match file {
Ok(file) => file, // succès : on a un descripteur de fichier
Err(e) => return Err(e), // échec d'ouverture : on renvoie l'erreur
};
let mut s = String::new();
return match file.read_to_string(&mut s) {
Ok(_) => Ok(s), // Le fichier n'a pas eu de problème pour être lu :
// on renvoie le contenu du fichier.
Err(e) => Err(e), // Le fichier a eu un problème pour être lu :
// on renvoie l'erreur.
let mut contents = String::new();
file.read_to_string(&mut contents) {
Ok(()) => {}, // succès : on a le contenu du fichier
Err(e) => return Err(e), // échec de lecture : on renvoie l'erreur
};
contents
}
\end{lstlisting}
Le pattern
\begin{lstlisting}[style=Rust, language=Rust]
let resultat = match faillible {
Ok(res) => res,
Err(err) => return Err(err),
};
// autre variante
let resultat = match faillible {
Some(res) => res,
None => return None,
};
\end{lstlisting}
est tellement fréquent qu'il a un raccourci : l'opérateur \texttt{?}.
On aurait pu écrire
\begin{lstlisting}[style=Rust, language=Rust]
fn read_file() -> Result<String, io::Error> {
let mut file = File::open("hello.txt")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
contents
}
\end{lstlisting}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment