Введение
Отдельные переменные подходят для небольших примеров, но в реальных программах часто требуется хранить связанные данные в одном значении. Структура Rust задаёт форму такой записи, а блок impl добавляет операции, относящиеся к этой записи.
Вы доработаете подготовленную модель InventoryItem, выполнив четыре небольших изменения: определите её поля, создадите корректный экземпляр, вычислите стоимость запасов через &self и измените количество через &mut self. В конце программа подтвердит и сохранённое состояние, и работу методов.
Определите запись Inventory
На этом шаге вы определите три элемента данных, которые относятся к одному инвентарному товару.
Перейдите в подготовленный проект Cargo и откройте его исходный файл:
cd /home/labex/project/inventory-item
nano src/main.rs
Объявление struct InventoryItem { ... } создаёт новый тип записи. Внутри фигурных скобок каждое поле содержит имя, двоеточие и тип. Каждый экземпляр InventoryItem должен содержать все эти поля.
Замените комментарий Step 1 следующими тремя строками полей:
name: String,
price_cents: u32,
quantity: u32,
Поле name хранит принадлежащий структуре текст, а два поля типа u32 — неотрицательные целые числа. Запятые в конце разделяют поля и упрощают последующее добавление или перестановку строк.
Сохраните файл с помощью Ctrl+O, нажмите Enter, затем выйдите с помощью Ctrl+X. Попросите Cargo проверить определения типов:
cargo check
Результат Finished без предупреждений означает, что форма структуры корректна и каждое подготовленное значение используется явно. В конструкторе по-прежнему находится todo! — временная отметка незавершённого кода. Rust допускает её наличие во время cargo check, но выполнение пути, который доходит до todo!, завершится паникой. Перед запуском программы на следующем шаге вы замените эту отметку.
Создайте полный экземпляр товара
На этом шаге вы превратите связанную функцию new в конструктор, возвращающий полный InventoryItem.
Блок impl InventoryItem объединяет операции, относящиеся к этому типу. new — это связанная функция, потому что она не получает self; её вызывают через тип: InventoryItem::new(...). Метод, например value_cents, получает self, поэтому его вызывают через значение: item.value_cents(). Тип возвращаемого значения Self внутри этого блока impl — другое имя для InventoryItem.
Откройте исходный файл:
nano src/main.rs
Внутри new полностью замените строку todo!(...) на:
Self { name, price_cents, quantity }
Это выражение структуры. Благодаря сокращённой записи полей Rust имя name означает name: name, если параметр и поле называются одинаково. В конце выражения нет точки с запятой, потому что оно является значением, возвращаемым функцией.
Сохраните файл и выйдите из редактора, затем проверьте и запустите программу:
cargo check
cargo run --quiet
Текущий вывод должен быть таким:
Preparing Notebook at 250 cents with 4 units
Item: Notebook
Unit price: 250 cents
Starting value: 0 cents
Adding 3 units
Quantity after restock: 4
Final value: 0 cents
Теперь экземпляр товара создан с переданными значениями полей. Нулевые значения и неизменившееся количество связаны с двумя оставшимися заготовками методов, поэтому на этом этапе такой результат ожидаем.
Вычислите стоимость с помощью &self
На этом шаге вы завершите метод, который читает два поля, не забирая владение экземпляром и не изменяя его.
Параметр &self — это общая ссылка на текущий InventoryItem. Это сокращённая запись заимствованного значения с конкретным типом &InventoryItem. Метод получает такую ссылку автоматически при вызове item.value_cents().
Откройте исходный файл:
nano src/main.rs
Внутри value_cents замените только заполнитель 0 на:
self.price_cents * self.quantity
Точечная запись обращается к полям заимствованного экземпляра. Умножение вычисляет общую стоимость в центах, а выражение становится возвращаемым значением метода типа u32, поскольку в конце нет точки с запятой.
Сохраните файл и выйдите из редактора, затем запустите программу:
cargo run --quiet
Теперь строки со стоимостью должны выглядеть так:
Starting value: 1000 cents
...
Final value: 1000 cents
Четыре единицы товара по 250 центов дают 1000 центов. Обе строки пока совпадают, потому что restock ещё не изменяет количество; это изменение будет выполнено на последнем шаге.
Пополните запас с помощью &mut self
На этом шаге вы завершите метод, который изменяет одно поле через эксклюзивный доступ к заимствованному экземпляру.
Получатель &mut self — это изменяемая ссылка на текущий товар. Она позволяет методу обновлять поля, не забирая владение экземпляром. Переменная в main уже объявлена с помощью let mut item, поэтому она может предоставить такой временный изменяемый доступ.
Откройте исходный файл:
nano src/main.rs
Замените комментарий Step 4 внутри restock на:
self.quantity += amount;
Оператор += прибавляет значение справа к существующему полю и сохраняет результат в этом же поле. Это инструкция, поэтому она заканчивается точкой с запятой.
Сохраните файл и выйдите из редактора, затем проверьте и запустите готовую модель:
cargo check
cargo run --quiet
Полный вывод должен быть таким:
Preparing Notebook at 250 cents with 4 units
Item: Notebook
Unit price: 250 cents
Starting value: 1000 cents
Adding 3 units
Quantity after restock: 7
Final value: 1750 cents
Количество изменяется с четырёх до семи, а тот же метод value_cents видит новое состояние. Изменяемое заимствование завершается после restock, поэтому последующий код может обычным образом читать товар.
Итоги
Вы определили структуру, которая объединяет связанные поля, создали значения с помощью конструктора Self, прочитали состояние методом &self и обновили его методом &mut self. Итоговый вывод об инвентаре подтвердил, что создание, вычисление и изменение состояния работают вместе.


