Gestion des erreurs
La méthode Account::from_string()
ne gère pas les erreurs et cela se reflète par le fait qu'elle renvoie systématiquement un Account
. Le code suivant provoquera probablement un panic!()
:
fn main() {
println!("{:?}", Account::from_string("johndoe"));
}
Afin de pouvoir gérer les erreurs, nous devons choisir quelle erreur remonter à l'appelant lorsqu'aucun :
n'est trouvé. Nous allons définir un type vide NoColon
dans le module account
qui nous servira pour signaler une erreur :
pub struct NoColon;
Cela nous permettra de renvoyer éventuellement un type Result<Account, NoColon>
lorsque cela sera approprié.
- Créez ce type
NoColon
dans le moduleaccount
.
FromStr
Plutôt que modifier la méthode Account::from_string()
, nous allons utiliser le trait prédéfini FromStr
pour convertir un &str
en Account
en implémentant FromStr
pour Account
.
- Implémentez
FromStr
pourAccount
et renvoyez une erreurNoColon
si aucun:
n'est trouvé.
Depuis main()
, il devrait être possible de faire :
fn main() {
match Account::from_str("johndoe") {
Ok(account) => println!("{account:?}"),
Err(e) => println!("Erreur {e:?}"),
}
}
Vous noterez que l'affichage de l'erreur n'est pas possible ; en effet, le type NoColon
n'implémente pas Debug
.
-
Faîtes dériver
Debug
automatiquement pour le typeNoColon
et vérifiez que le programme fonctionne comme attendu. -
Vous pouvez supprimer la méthode
Account::from_string()
que nous n'utiliserons plus. -
Changez le type de retour de
main()
pour qu'il soit maintenantResult<(), NoColon>
. Utilisez?
pour retourner prématurément en cas d'erreur. Cela se passera bien carNoColon
implémenteDebug
, ce qui est obligatoire si on veut l'utiliser dans le type de retour demain()
pour pouvoir afficher l'erreur qui a provoqué la fin demain()
.
fn main() -> Result<(), NoColon> {
println!("{:?}", Account::from_str("johndoe")?);
Ok(())
}
Gestion de la ligne de commande
- En utilisant l'itérateur
std::env::args()
et la méthodecollect()
, construisez dansmain()
un vecteur (Vec
) deAccount
à partir des arguments donnés sur la ligne de commande et affichez ce vecteur (si les éléments implémententDebug
, le vecteur implémente égalementDebug
). On n'oubliera pas qu'on peut passer d'une variableString
à sa représentation&str
en utilisant la méthodeas_str()
.
On se souviendra qu'il est possible d'indiquer le type de résultat voulu à la méthode collect()
de deux manières : soit le contexte est non-ambigu et permet de déterminer le type de collection à construire, soit il est possible d'indiquer un type complet ou partiel avec l'opérateur turbofish ::<>
:
fn main() { // Utilisation d'un contexte totalement explicite let v: Vec<u32> = (1..=10).collect(); println!("{v:?}"); // Utilisation d'un contexte partiellement explicite let v: Vec<_> = (1u32..=10).collect(); println!("{v:?}"); // Utilisation du turbofish let v = (1u32..=10).collect::<Vec<_>>(); println!("{v:?}"); }
On peut bien sûr préciser des types plus complexes, comme Vec<Result<_, _>>
. Voici un exemple d'utilisation de notre programme :
$ cargo run -- johndoe:complex:password johndoe johndoe:password
[
Ok(
Account {
login: "johndoe",
password: "complex:password",
},
),
Err(
NoColon,
),
Ok(
Account {
login: "johndoe",
password: "password",
},
),
]
On pourra noter l'utilisation de --
sur la ligne de commande de cargo run
pour séparer les arguments passés à cargo run
de ceux passés à l'application. Ce n'est pas obligatoire ici mais le devient dès lors qu'on voudra passer des options commençant par -
à notre application.
- Si vous ne l'avez pas fait précédemment, utilisez le fait que
collect()
appliqué à un itérateur sur des valeurs de typeResult<T, E>
peut produire unResult<Collection<T>, E>
oùCollection
est tout type de collection commeVec
. Ce collecteur s'arrête et renvoie une erreur dès lors qu'il en rencontre une dans l'itérateur. Dans ce cas, utilisez?
pour quittermain()
avec l'erreur renvoyée, et sinon affichez les comptes utilisateur décodés.
$ cargo run -- johndoe:complex:password johndoe johndoe:password
Error: NoColon
$ cargo run -- johndoe:complex:password johndoe:password
[
Account {
login: "johndoe",
password: "complex:password",
},
Account {
login: "johndoe",
password: "password",
},
]