Menu
Coddy logo textTech

Le pattern Newtype

Fait partie de la section Object Oriented Programming du Journey Rust de Coddy. Leçon 54 sur 61.

Parfois, vous avez deux valeurs du même type qui représentent des choses complètement différentes. L'adresse email d'un utilisateur et son mot de passe peuvent tous deux être des valeurs String, mais les intervertir accidentellement dans un appel de fonction serait un bug grave. Le patron Newtype résout ce problème en créant des types distincts qui encapsulent des types primitifs.

Un newtype est simplement une structure tuple avec un seul champ :

struct Password(String);
struct Email(String);

Bien que tous deux encapsulent une String, le compilateur les traite comme des types complètement différents. Vous ne pouvez pas transmettre accidentellement un Password là où un Email est attendu : le compilateur détectera l'erreur.

fn send_reset_email(email: Email, password: Password) {
    // ...
}

let email = Email(String::from("user@example.com"));
let pass = Password(String::from("secret123"));

send_reset_email(email, pass);  // Ordre correct imposé par les types
// send_reset_email(pass, email);  // Ne compilera pas !

Pour access la valeur inner, utilise .0 puisqu'il s'agit d'une structure tuple :

let pass = Password(String::from("secret123"));
println!("Length: {}", pass.0.len());

Vous pouvez également ajouter des méthodes à votre newtype, en lui donnant un comportement propre à ce qu’il représente, comme la validation ou le formatage, ce qui n’aurait pas de sens avec un simple String.

challenge icon

Défi

Facile

Construisons un système d’inscription d’utilisateurs sûr du point de vue des types en utilisant le modèle Newtype ! Vous allez créer des types distincts pour Username et UserId afin d’éviter de les mélanger accidentellement, même si tous deux encapsulent des valeurs simples.

Vous allez organiser votre code sur deux fichiers :

  • user_types.rs : Define deux structs newtype public : Username encapsulant un String et UserId encapsulant un u32. Ajoutez à chacun une method appelée value qui returns une référence aux données inner (pour Username, return &String ; pour UserId, return &u32). Cela fournit un access contrôlé aux valeurs encapsulées.
  • main.rs : Importez votre module user_types et créez une function appelée display_user qui takes un UserId comme premier parameter et un Username comme second parameter. La function should afficher les informations de l’user. Créez ensuite des instances des deux types à l’aide des inputs fournis et appelez votre function.

L’idée essentielle est que, même si UserId et Username sont de simples wrappers, le compilateur les traite comme des types complètement différents. Vous ne pouvez pas passer accidentellement un nom d’utilisateur là où un ID est attendu !

Votre function display_user should afficher dans ce format :

User #{id}: {username}

Par exemple, avec les inputs 42 et alice_dev :

User #42: alice_dev

Et avec les inputs 1001 et bob_smith :

User #1001: bob_smith

Vous recevrez deux inputs : l’ID de l’user (à parse en u32) et la chaîne du nom d’utilisateur.

Essayez vous-même

mod user_types;

use user_types::{Username, UserId};

// TODO: Créez une fonction appelée `display_user` qui prend :
// - Premier paramètre : UserId
// - Deuxième paramètre : Username
// La fonction doit afficher : "User #{id}: {username}"
// Utilisez la méthode .value() pour accéder aux valeurs internes


fn main() {
    // Lire l'entrée
    let mut id_input = String::new();
    std::io::stdin().read_line(&mut id_input).expect("Failed to read line");
    let id: u32 = id_input.trim().parse().expect("Invalid number");
    
    let mut username_input = String::new();
    std::io::stdin().read_line(&mut username_input).expect("Failed to read line");
    let username = username_input.trim().to_string();
    
    // TODO: Créez des instances de UserId et Username en utilisant les entrées
    
    // TODO: Appelez display_user avec les arguments corrects
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Object Oriented Programming

Entraînez-vous par vous-même : Compilateur Rust en ligne