Упражнение: инвентарь волшебника
В этом упражнении вы будете управлять инвентарём волшебника, используя то, что узнали о заимствовании и владении.
-
У волшебника есть коллекция заклинаний. Нужно реализовать функции для добавления заклинаний в инвентарь и для их применения.
-
У заклинаний ограниченное число использований. Когда у заклинания не остаётся использований, его нужно удалить из инвентаря волшебника.
// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0
struct Spell {
name: String,
cost: u32,
uses: u32,
}
struct Wizard {
spells: Vec<Spell>,
mana: u32,
}
impl Wizard {
fn new(mana: u32) -> Self {
Wizard { spells: vec![], mana }
}
// TODO: Реализуйте `add_spell`, чтобы он забирал владение заклинанием и добавлял его в
// инвентарь волшебника.
fn add_spell(..., spell: ...) {
todo!()
}
// TODO: Реализуйте `cast_spell`, чтобы он заимствовал заклинание из инвентаря и
// применял его. Мана волшебника должна уменьшиться на стоимость заклинания, а
// число использований заклинания --- на 1.
//
// Если у волшебника недостаточно маны, заклинание не срабатывает.
// Если у заклинания не осталось использований, оно удаляется из инвентаря.
fn cast_spell(..., name: ...) {
todo!()
}
}
fn main() {
let mut merlin = Wizard::new(100);
let fireball = Spell { name: String::from("Fireball"), cost: 10, uses: 2 };
let ice_blast = Spell { name: String::from("Ice Blast"), cost: 15, uses: 1 };
merlin.add_spell(fireball);
merlin.add_spell(ice_blast);
merlin.cast_spell("Fireball"); // Успешно применено
merlin.cast_spell("Ice Blast"); // Успешно применено, затем удалено
merlin.cast_spell("Ice Blast"); // Не срабатывает (не найдено)
merlin.cast_spell("Fireball"); // Успешно применено, затем удалено
merlin.cast_spell("Fireball"); // Не срабатывает (не найдено)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add_spell() {
let mut wizard = Wizard::new(10);
let spell = Spell { name: String::from("Fireball"), cost: 5, uses: 3 };
wizard.add_spell(spell);
assert_eq!(wizard.spells.len(), 1);
}
#[test]
fn test_cast_spell() {
let mut wizard = Wizard::new(10);
let spell = Spell { name: String::from("Fireball"), cost: 5, uses: 3 };
wizard.add_spell(spell);
wizard.cast_spell("Fireball");
assert_eq!(wizard.mana, 5);
assert_eq!(wizard.spells.len(), 1);
assert_eq!(wizard.spells[0].uses, 2);
}
#[test]
fn test_cast_spell_insufficient_mana() {
let mut wizard = Wizard::new(10);
let spell = Spell { name: String::from("Fireball"), cost: 15, uses: 3 };
wizard.add_spell(spell);
wizard.cast_spell("Fireball");
assert_eq!(wizard.mana, 10);
assert_eq!(wizard.spells.len(), 1);
assert_eq!(wizard.spells[0].uses, 3);
}
#[test]
fn test_cast_spell_not_found() {
let mut wizard = Wizard::new(10);
wizard.cast_spell("Fireball");
assert_eq!(wizard.mana, 10);
}
#[test]
fn test_cast_spell_removal() {
let mut wizard = Wizard::new(10);
let spell = Spell { name: String::from("Fireball"), cost: 5, uses: 1 };
wizard.add_spell(spell);
wizard.cast_spell("Fireball");
assert_eq!(wizard.mana, 5);
assert_eq!(wizard.spells.len(), 0);
}
}
This slide and its sub-slides should take about 40 minutes.
- Цель этого упражнения — отработать базовые концепции владения и заимствования, в частности правило, что нельзя изменять коллекцию, удерживая ссылку на один из её элементов.
add_spellдолжен забрать владениеSpellи переместить его в инвентарьWizard.cast_spell— ядро упражнения. Ему нужно:- Найти заклинание (по индексу или по ссылке).
- Проверить ману и уменьшить её.
- Уменьшить
usesзаклинания. - Удалить заклинание, если
uses == 0.
- Конфликт заимствований: если студенты попытаются удерживать ссылку на заклинание (например,
let spell = &mut self.spells[i]), а затем вызватьself.spells.remove(i), пока эта ссылка ещё «жива» в той же области видимости, анализатор заимствований выдаст ошибку. Это хороший повод показать, как структурировать код так, чтобы удовлетворить анализатор заимствований (например, используя индексы или обеспечив завершение заимствования до изменения).