Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Упражнение: инвентарь волшебника

В этом упражнении вы будете управлять инвентарём волшебника, используя то, что узнали о заимствовании и владении.

  • У волшебника есть коллекция заклинаний. Нужно реализовать функции для добавления заклинаний в инвентарь и для их применения.

  • У заклинаний ограниченное число использований. Когда у заклинания не остаётся использований, его нужно удалить из инвентаря волшебника.

// 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 — ядро упражнения. Ему нужно:
    1. Найти заклинание (по индексу или по ссылке).
    2. Проверить ману и уменьшить её.
    3. Уменьшить uses заклинания.
    4. Удалить заклинание, если uses == 0.
  • Конфликт заимствований: если студенты попытаются удерживать ссылку на заклинание (например, let spell = &mut self.spells[i]), а затем вызвать self.spells.remove(i), пока эта ссылка ещё «жива» в той же области видимости, анализатор заимствований выдаст ошибку. Это хороший повод показать, как структурировать код так, чтобы удовлетворить анализатор заимствований (например, используя индексы или обеспечив завершение заимствования до изменения).