Rust: с нуля до своего органайзера Урок 28 из 35
Методы: действия рядом с Task
Урок 28. Создать задачу, прочитать её состояние и завершить её через методы.
Текст переведён с помощью ИИ.
Предварительно: уроки 14, 20, 21 и 27. Если разница между & и &mut забылась, повторите урок 21.
Образ и опорная карта
У карточки задачи есть не только заполненные графы, но и действия: прочитать состояние, отметить выполнение. Метод — функция, связанная с типом и вызываемая через значение этого типа. Блок impl Task { ... } собирает такие действия рядом с описанием Task; impl означает «реализация». Образ ограничен: карточка сама ничего не делает — код метода явно выполняется при вызове.
struct Task → impl Task → Task::new(...) → task.is_done() → task.finish(). Перескажите карту. Функция new внутри impl без параметра self связана с типом и вызывается как Task::new(...); :: показывает путь от типа к функции, как в String::from(...). Это связанная функция, а не метод экземпляра. Метод имеет первый параметр self: &self берёт задачу для чтения, &mut self — для изменения. При вызове task.is_done() или task.finish() Rust передаёт task как этот первый параметр автоматически.
Читаем и меняем задачу
Сохраните прежний src/main.rs проекта organizer, замените его целиком и запустите cargo run рядом с Cargo.toml. Других файлов и зависимостей нет. Сначала предскажите результат.
new принимает название и минуты, создаёт Task с done: false и возвращает его. В Task { title, minutes, done: false } два имени без : — сокращённая запись полей: это title: title, minutes: minutes, потому что имена параметров совпадают с именами полей. Здесь переданы известные нам подходящие значения; функция пока не проверяет пользовательский ввод. is_done возвращает копируемый bool и не меняет задачу. finish меняет поле, поэтому требует &mut self и изменяемой переменной task.
struct Task {
title: String,
minutes: u32,
done: bool,
}
impl Task {
fn new(title: String, minutes: u32) -> Task {
Task {
title,
minutes,
done: false,
}
}
fn is_done(&self) -> bool {
self.done
}
fn finish(&mut self) {
self.done = true;
}
}
fn main() {
let mut task = Task::new(String::from("Чтение"), 20);
println!(
"{}: {} мин, готово {}",
task.title,
task.minutes,
task.is_done()
);
task.finish();
println!("После: {}", task.is_done());
}
Чтение: 20 мин, готово false
После: true
title передаёт владение строкой в новый экземпляр. У finish нет стрелки результата; значит, он возвращает () из урока 14. Заимствование через &mut self заканчивается после вызова, и is_done снова может читать задачу.
Когда &mut self нельзя взять
Если переменная не объявлена mut, метод изменения не может получить изменяемую ссылку. Этот отдельный пример намеренно не компилируется с E0596:
struct Task { done: bool }
impl Task {
fn finish(&mut self) { self.done = true; }
}
fn main() {
let task = Task { done: false };
task.finish();
}
Добавление mut к task разрешит вызов. Но методы не отменяют правил владения: &self не меняет поля, а &mut self требует исключительного доступа на время вызова. new здесь не запрещает 0 минут. Полную проверку значений и сообщение об ошибке добавим, когда освоим Result.
Проверьте себя
- Зачем
newвызывается черезTask::, аfinishчерезtask.? - Какой метод требует изменяемой переменной?
- Почему после
finish()можно вызватьis_done()?
Проверьте: new не принимает экземпляр; finish берёт &mut self; его временное заимствование закончилось. Проговорите цепочку из опорной карты без страницы.
Задание
Обязательное. Объявите Task { title: String, done: bool }. В impl Task сделайте new(title: String) -> Task, метод is_done(&self) -> bool и finish(&mut self). Создайте изменяемую задачу Прогулка, выведите Прогулка: false, завершите её и выведите Прогулка: true. Оба значения состояния получите через метод.
Изменение. Уберите mut перед переменной, прочитайте ошибку, затем верните mut.
Подсказка
new создаёт Task { title, done: false }. finish записывает self.done = true;. Вызовите task.is_done() до и после task.finish().
Эталон после попытки
struct Task {
title: String,
done: bool,
}
impl Task {
fn new(title: String) -> Task {
Task { title, done: false }
}
fn is_done(&self) -> bool {
self.done
}
fn finish(&mut self) {
self.done = true;
}
}
fn main() {
let mut task = Task::new(String::from("Прогулка"));
println!("{}: {}", task.title, task.is_done());
task.finish();
println!("{}: {}", task.title, task.is_done());
}
Прогулка: false
Прогулка: true
Данные и относящиеся к ним действия находятся рядом. Официальный разбор методов.
Если вы нашли ошибку или опечатку в тексте статьи, то сообщите нам об этом
Комментарии (0)
Войдите, чтобы оставить комментарий →
Пока нет комментариев. Будьте первым.