Structure de compte utilisateur

On souhaite écrire un programme qui vérifie certaines propriétés sur un ensemble de comptes utilisateur. On dispose des identifiants (login) et des mots de passe (password) de ces comptes.

Nous allons commencer par créer la structure permettant de décrire un compte utilisateur :

  1. Créez un nouveau module account dans le projet pwdchk. Le corps du module devra se trouver soit dans src/account.rs, soit dans src/account/mod.rs.
  2. Référencez le module account depuis src/main.rs pour qu'il soit compilé lors d'un cargo build.
  3. Créez une structure Account contenant deux champs de type String : login et password.
  4. Ajoutez dans l'implémentation de Account une méthode de classe new() qui reçoit le login et le password en paramètres de type &str et renvoie un nouvel Account reprenant ces informations.
struct Account { /* Ajouter les champs ici */ }
impl Account {
  fn new(login: &str, password: &str) -> Self {
    todo!()   // À implémenter
  }
}
  1. Créez une variable de type Account depuis main() en utilisant Account::new(). À quel endroit faut-il augmenter la visibilité en ajoutant pub pour pouvoir créer une variable de type Account et appeler la méthode new() ?
  2. Depuis main(), affichez le compte nouvellement créé :
fn main() {
  let account = Account::new("johndoe", "super:complex:password");
  println!("{account:?}");
}

qui devra afficher

Account { login: "johndoe", password: "super:complex:password" }

On notera qu'on demande à utiliser le trait Debug de Account grâce à l'utilisation de {:?}. Ce trait peut être dérivé automatiquement en précédant la définition de la structure par #derive(Debug) :

#![allow(unused)]
fn main() {
#[derive(Debug)]
struct Account {
  /* Ajouter les champs ici */
}
}

On aurait pu utiliser {} mais cela aurait nécessité d'implémenter le trait Display pour Account. Contrairement à Debug, Display ne peut pas être dérivé automatiquement.

Différentes écritures possibles

Les macros de type format! (dont println!, write!, panic!, etc.) possèdent plusieurs méthodes pour désigner leurs arguments. Toutes les formes ci-dessous sont équivalentes :

fn main() {
  let account = Account::new("johndoe", "super:complex:password");
  println!("{:?}", account);       // Each {} uses a new argument
  println!("{0:?}", account);      // Use the first argument (0)
  println!("{a:?}", a = account);  // Use the `a` named argument
  println!("{account:?}");         // Take argument from named arguments or context
}

La dernière forme ne permet pas de mettre des expressions complexes entre accolades, seules des variables peuvent être utilisées.

Forme alternative et arguments

Il est également possible d'opter pour une forme mieux formattée mais moins compacte d'affichage de la structure en utilisant {:#?}, le modificateur # signifiant ici l'utilisation de la forme alternative :

fn main() {
  let account = Account::new("johndoe", "super:complex:password");
  println!("account = {account:#?}");
}

On obtiendra alors la sortie :

Account {
    login: "johndoe",
    password: "super:complex:password",
}

Cette forme alternative est disponible pour un certain nombre de formats. Par exemple, le programme suivant

fn main() {
  println!("forme normale hexa = {0:x}, forme alternative = {0:#x}", 42);
}

affichera

forme normale hexa = 2a, forme alternative = 0x2a

On notera que l'utilisation d'un argument positionnel (0) évite de répéter l'argument.