Parcours
/
Rust
Rust
/
Exercices
/
Jeu de rôle
Jeu de rôle

Jeu de rôle

Exercice d'apprentissage

Introduction

Références nulles

Si tu as déjà utilisé un autre langage de programmation (C/C++, Python, Java, Ruby, Lisp, etc.), il est probable que tu aies déjà rencontré null ou nil. L'utilisation de null ou nil est la façon dont ces langages indiquent qu'une variable donnée n'a pas de valeur. Cependant, il est alors facile (et fréquent) d'utiliser par accident une variable qui pointe vers null. Comme tu peux l'imaginer, essayer d'appeler une fonction qui n'existe pas ou d'accéder à une valeur qui n'existe pas peut entraîner toutes sortes de bugs et de plantages. Le créateur de null est même allé jusqu'à l'appeler son « erreur à un milliard de dollars ».

Le type Option

Pour éviter ces problèmes, Rust n'utilise pas de références nulles. Cependant, il a toujours besoin d'un moyen sûr d'indiquer qu'une variable donnée n'a pas de valeur. C'est là que Option entre en jeu. Au lieu d'avoir une variable dépourvue de valeur, les variables Rust peuvent utiliser l'énumération Option. Cette énumération a deux variantes : None, l'équivalent de null en Rust ; et Some(T), où T est une valeur de n'importe quel type.

Elle ressemble à ceci :

enum Option<T> {
    None,
    Some(T),
}

Tu peux voir Option comme une couche de sécurité entre toi et les problèmes que peuvent causer les références nulles, tout en conservant leur utilité conceptuelle.

Utiliser Option

Affecter None à une variable est assez simple :

let nothing: Option<u32> = None;  // Variable nothing is set to None

Cependant, si tu veux que le type Option porte une valeur, tu ne peux pas affecter cette valeur directement. Une variable de type Option et, disons, une variable de type i32 ne sont pas équivalentes. Tu devras utiliser Some :

let wrong_way: Option<i32> = -4; // This will not work

let right_way: Option<i32> = Some(-4); // This will work
let another_right_way = Some(-4); // Compiler infers that this is Option<i32>

C'est aussi pour cette raison que ce qui suit ne fonctionnera pas :

let number = 47;
let option_number = Some(15);

let compile_error = number + option_number; // Cannot add an i32 and an Option<i32> - they are of different types

Si tu veux récupérer la valeur contenue dans Some, tu devras d'abord vérifier qu'elle existe :

let mut some_words = Some("choose something to say"); // some_words set to something

match some_words {
    Some(str) => println!("Here, we will {}", str),
    None => println!("I've got nothing to say"),
} // Prints "Here, we will choose something to say"

some_words = None; // some_words now set to None

// exactly the same match block as above
match some_words {
    Some(str) => println!("Here, we will {}", str),
    None => println!("I've got nothing to say"),
} // Prints "I've got nothing to say"

Outre match, Rust dispose d'autres outils pour vérifier et accéder aux valeurs contenues dans Option, mais match devrait t'être familier maintenant.

De plus, vois-y une démonstration de la raison pour laquelle Rust utilise Option au lieu d'une référence nulle. L'idée, c'est que tu dois vérifier si la variable Option vaut Some (auquel cas tu peux extraire et utiliser la valeur qu'elle contient) ou None. Dans tout autre cas, ton programme ne compilera pas ; le compilateur te protège de null.

Instructions

Tu travailles à l'implémentation d'un jeu de rôle. Le personnage du joueur est représenté par ce qui suit :

pub struct Player {
    health: u32,
    mana: Option<u32>,
    level: u32,
}

Dans ce jeu, les joueurs doivent atteindre le niveau 10 avant de débloquer une réserve de mana, ce qui leur permet ensuite de commencer à lancer des sorts. Avant ce niveau, le mana du Player vaut None.

Tu travailles sur deux fonctionnalités de ce jeu : le mécanisme de réanimation et le mécanisme de lancement de sorts.

La méthode revive doit vérifier que le Player est bien mort (ses points de vie ont atteint 0) et, si c'est le cas, renvoyer une nouvelle instance de Player avec 100 points de vie. Si le niveau du Player est supérieur ou égal à 10, il doit également être réanimé avec 100 mana. Si le niveau du Player est inférieur à 10, son mana doit valoir None. La méthode revive doit conserver le niveau du Player.

let dead_player = Player { health: 0, mana: None, level: 2 };
dead_player.revive()
// Returns Player { health: 100, mana: None, level: 2 }

Si la méthode revive est appelée sur un Player dont les points de vie sont supérieurs ou égaux à 1, elle doit renvoyer None.

let alive_player = Player { health: 1, mana: Some(15), level: 11 };
alive_player.revive()
// Returns None

La méthode cast_spell prend une référence mutable vers le Player ainsi qu'un paramètre mana_cost indiquant combien de mana le sort coûte. Elle renvoie la quantité de dégâts que le sort lancé inflige, qui vaudra toujours deux fois le coût en mana du sort si celui-ci est lancé avec succès.

  • Si le joueur n'a pas accès à une réserve de mana, tenter de lancer le sort doit diminuer ses points de vie du coût en mana du sort. Les dégâts renvoyés doivent être de 0.

    let not_a_wizard_yet = Player { health: 79, mana: None, level: 9 };
    assert_eq!(not_a_wizard_yet.cast_spell(5), 0)
    assert_eq!(not_a_wizard_yet.health, 74);
    assert_eq!(not_a_wizard_yet.mana, None);
    
  • Si le joueur dispose d'une réserve de mana mais pas d'assez de mana, la méthode ne doit pas modifier la réserve et renvoyer 0 à la place.

    let low_mana_wizard = Player { health: 93, mana: Some(3), level: 12 };
    assert_eq!(low_mana_wizard.cast_spell(10), 0);
    assert_eq!(low_mana_wizard.health, 93);
    assert_eq!(low_mana_wizard.mana, Some(3));
    
  • Sinon, le mana_cost doit être déduit de la réserve de mana du Player et la quantité de dégâts appropriée doit être renvoyée.

    let wizard = Player { health: 123, mana: Some(30), level: 18 };
    assert_eq!(wizard.cast_spell(10), 20);
    assert_eq!(wizard.health, 123);
    assert_eq!(wizard.mana, Some(20));
    

Amuse-toi bien !

Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet
Rust Exercism

Prêt à commencer Jeu de rôle ?

Inscris-toi sur Exercism pour apprendre et maîtriser Rust avec 99 exercices, et un vrai mentorat humain, le tout gratuitement.