Rust: с нуля до своего органайзера Урок 37 из 60
Список задач в памяти: добавить, изменить, завершить, удалить
Урок 37. Выполнить четыре действия с задачами и увидеть, как удаление сдвигает места в списке.
Текст переведён с помощью ИИ.
Предварительно: уроки 21, 24, 27, 31–33 и 36. Если забыли изменяемое заимствование, вернитесь к уроку 21.
Образ и опорная карта
Представьте карточки задач, разложенные в ряд. Можно положить новую карточку, переписать название, поставить отметку и убрать карточку. Vec<Task> — такой ряд в памяти программы. Карточки пока исчезают после закрытия программы: файлы появятся позднее. Образ ограничен: место в ряду не является постоянным номером карточки.
Проверка → изменение Vec<Task> → показ результата; закрытие программы → данные в памяти исчезают. Task объединяет title: String и done: bool. Поле done истинно после завершения. &mut Vec<Task> даёт функции временный исключительный доступ к списку для изменения, а &[Task] — только чтение среза списка. get_mut(index) возвращает Option<&mut Task>: место может отсутствовать. tasks.remove(index) удаляет элемент и сдвигает следующие влево; перед ним мы проверяем границу, поскольку сам remove при неверном месте остановил бы программу. usize — тип места в списке из урока 15. Result<(), String> означает: успешное действие не возвращает данных (()), ошибка возвращает объяснение. for task in tasks читает каждую карточку по порядку.
Четыре действия
Сохраните прежний src/main.rs и замените целиком. Запустите cargo run в проекте organizer. Новых зависимостей нет. Предскажите, какая карточка останется после удаления места 0. [ ] означает ожидающую задачу, а [готово] — завершённую. В этом учебном main .expect(...) достаёт успешный Ok, но остановит программу на Err; он уместен только для заранее известных верных данных, а не для ввода пользователя.
struct Task {
title: String,
done: bool,
}
fn add(tasks: &mut Vec<Task>, title: &str) -> Result<(), String> {
if title.trim().is_empty() {
return Err(String::from("Название пусто"));
}
tasks.push(Task {
title: String::from(title),
done: false,
});
Ok(())
}
fn rename(tasks: &mut Vec<Task>, index: usize, title: &str) -> Result<(), String> {
match tasks.get_mut(index) {
Some(task) => {
task.title = String::from(title);
Ok(())
}
None => Err(String::from("Такого места нет")),
}
}
fn complete(tasks: &mut Vec<Task>, index: usize) -> Result<(), String> {
match tasks.get_mut(index) {
Some(task) => {
task.done = true;
Ok(())
}
None => Err(String::from("Такого места нет")),
}
}
fn remove(tasks: &mut Vec<Task>, index: usize) -> Result<(), String> {
if index >= tasks.len() {
return Err(String::from("Такого места нет"));
}
tasks.remove(index);
Ok(())
}
fn show(tasks: &[Task]) {
for task in tasks {
if task.done {
println!("[готово] {}", task.title);
} else {
println!("[ ] {}", task.title);
}
}
}
fn main() {
let mut tasks = Vec::new();
add(&mut tasks, "Купить хлеб").expect("valid title");
add(&mut tasks, "Прочитать главу").expect("valid title");
rename(&mut tasks, 1, "Прочитать две главы").expect("existing position");
complete(&mut tasks, 0).expect("existing position");
remove(&mut tasks, 0).expect("existing position");
show(&tasks);
}
[ ] Прочитать две главы
В учебном main вызовы .expect(...) допустимы лишь потому, что здесь мы заранее задали существующие места и непустые названия. Это метод Result, который достаёт Ok, но при Err останавливает программу; в настоящем вводе так поступать нельзя. В задании мы обработаем ошибки явно. После удаления второй элемент переходит на место 0. Это причина в следующем уроке ввести устойчивый ID.
Проверка понимания
- Почему
get_mutвозвращаетOption, а не сразу&mut Task? - Что случится с местом второй карточки после удаления первой?
- Останутся ли эти задачи после следующего запуска?
Места может не быть; вторая карточка перейдёт на место 0; нет, пока нет сохранения. Восстановите опорную карту.
Задание
Обязательное. Дополните rename: отклоняйте название, состоящее только из пробелов, не меняя прежнее. После успешных действий попробуйте переименовать место 0 в " " и завершить место 9. Выведите обе ошибки и затем список; прежнее название должно сохраниться.
Подсказка
Проверку title.trim().is_empty() поставьте до get_mut. Ошибку из Result выведите через match, как в уроке 32.
Эталон после попытки
struct Task {
title: String,
done: bool,
}
fn add(tasks: &mut Vec<Task>, title: &str) -> Result<(), String> {
if title.trim().is_empty() {
return Err(String::from("Название пусто"));
}
tasks.push(Task {
title: String::from(title),
done: false,
});
Ok(())
}
fn rename(tasks: &mut Vec<Task>, index: usize, title: &str) -> Result<(), String> {
if title.trim().is_empty() {
return Err(String::from("Название пусто"));
}
match tasks.get_mut(index) {
Some(task) => {
task.title = String::from(title);
Ok(())
}
None => Err(String::from("Такого места нет")),
}
}
fn complete(tasks: &mut Vec<Task>, index: usize) -> Result<(), String> {
match tasks.get_mut(index) {
Some(task) => {
task.done = true;
Ok(())
}
None => Err(String::from("Такого места нет")),
}
}
fn remove(tasks: &mut Vec<Task>, index: usize) -> Result<(), String> {
if index >= tasks.len() {
return Err(String::from("Такого места нет"));
}
tasks.remove(index);
Ok(())
}
fn show(tasks: &[Task]) {
for task in tasks {
if task.done {
println!("[готово] {}", task.title);
} else {
println!("[ ] {}", task.title);
}
}
}
fn main() {
let mut tasks = Vec::new();
add(&mut tasks, "Купить хлеб").expect("valid title");
add(&mut tasks, "Прочитать главу").expect("valid title");
rename(&mut tasks, 1, "Прочитать две главы").expect("existing position");
complete(&mut tasks, 0).expect("existing position");
remove(&mut tasks, 0).expect("existing position");
for result in [rename(&mut tasks, 0, " "), complete(&mut tasks, 9)] {
if let Err(message) = result {
println!("Ошибка: {message}");
}
}
show(&tasks);
}
Ошибка: Название пусто
Ошибка: Такого места нет
[ ] Прочитать две главы
Если вы нашли ошибку или опечатку в тексте статьи, то сообщите нам об этом
Комментарии (0)
Войдите, чтобы оставить комментарий →
Пока нет комментариев. Будьте первым.