Rust: с нуля до своего органайзера Урок 24 из 35
Растущий список Vec
Урок 24. Добавлять и удалять названия задач, не задавая число мест заранее.
Текст переведён с помощью ИИ.
Предварительно: уроки 15, 19–23. Если срез и его владелец смешиваются, повторите урок 23.
Образ и опорная карта
В лотке на три ячейки из урока 15 четвёртой ячейки нет. Список на листе можно продолжить новыми строками. Vec — владеющий растущий список значений одного типа; он хранит длину и при необходимости получает больше места. Образ ограничен: рост может перенести хранилище, а удаление из середины сдвигает следующие элементы. Индексы не служат постоянными номерами задач.
Пустой список → push → длина → чтение через & → remove → новые позиции. Запомните карту и воспроизведите её перед заданием. В Vec<String> угловые скобки <...> называют тип каждого элемента, здесь String; это не знаки сравнения. Такая форма называется параметризованным типом: один и тот же Vec может хранить элементы другого типа, но в одном списке тип у всех элементов один. Vec::new() создаёт пустой список. push добавляет элемент в конец, .len() сообщает число элементов.
От пустого списка к двум задачам
В проекте organizer сохраните прежний src/main.rs, замените весь файл следующим примером и в папке с Cargo.toml выполните cargo run. Другие файлы и зависимости не меняются. Предскажите вывод без сообщений Cargo.
for title in &titles создаёт доступ для чтения к списку и по очереди даёт ссылку на каждый String. Здесь & не передаёт владение списком циклу; после обхода его ещё можно использовать. String::from создаёт владеющие названия; push передаёт их в список.
fn main() {
let mut titles: Vec<String> = Vec::new();
titles.push(String::from("Чтение"));
titles.push(String::from("Отдых"));
println!("Задач: {}", titles.len());
for title in &titles {
println!("Задача: {title}");
}
}
Задач: 2
Задача: Чтение
Задача: Отдых
mut нужен для изменения состава списка. Как и массив, Vec<String> хранит упорядоченные элементы одного типа и допускает повтор названия. В отличие от массива, длина может изменяться во время работы. Vec — пока список только в памяти; закрытие программы не сохранит задачи. Файлы появятся позднее.
Удаление и владение
remove(1) удаляет элемент с индексом 1 и возвращает его String вызывающему коду. Следующие элементы сдвигаются влево. Индекс должен быть меньше .len(), иначе программа остановится с panic. Поэтому прежде чем удалять индекс, полученный от пользователя, нужно проверить границу; здесь индекс задан нами. После удаления список по-прежнему владеет оставшимися элементами.
Если название уже лежало в переменной, push(title) перенесёт его владение в список. Эта отдельная программа намеренно не компилируется с E0382:
fn main() {
let title = String::from("Чтение");
let mut titles: Vec<String> = Vec::new();
titles.push(title);
println!("{title}");
}
Список теперь владеет этим String, а прежнее имя title больше не может им пользоваться. Если надо лишь прочитать название из списка, обойдите его по &titles; не копируйте все строки без необходимости. Нельзя одновременно использовать ссылку на элемент и менять состав списка так, чтобы он мог переместить хранилище; правило заимствования из урока 21 это предотвращает.
Проверка понимания
- Чем
Vec<String>отличается от[String; 3]? - Что означает
Stringмежду<и>? - Почему индекс удалённой задачи не может быть её постоянным ID?
Проверьте: растущая и фиксированная длина; тип каждого элемента; удаление сдвигает позиции. Не путайте размер списка с номером последней записи.
Задание
Обязательное. Создайте пустой Vec<String>, добавьте Чтение, Прогулка, Отдых. Удалите элемент с индексом 1 через remove, выведите Удалено: Прогулка, затем оставшиеся названия по порядку и Осталось: 2. Для обхода используйте &titles. Не печатайте названия вместо обхода готовыми строками.
Граница. Объясните, что случится при remove(3) у списка длины 3 и почему нельзя проверять допустимость по <= titles.len().
Подсказка
Сохраните результат remove(1) в removed. После удаления пройдите for title in &titles. Число элементов получите из .len().
Эталон после попытки
fn main() {
let mut titles: Vec<String> = Vec::new();
titles.push(String::from("Чтение"));
titles.push(String::from("Прогулка"));
titles.push(String::from("Отдых"));
let removed = titles.remove(1);
println!("Удалено: {removed}");
for title in &titles {
println!("Задача: {title}");
}
println!("Осталось: {}", titles.len());
}
Удалено: Прогулка
Задача: Чтение
Задача: Отдых
Осталось: 2
Удалённым значением владеет removed, а оставшимися — список. Официальный разбор Vec.
Если вы нашли ошибку или опечатку в тексте статьи, то сообщите нам об этом
Комментарии (0)
Войдите, чтобы оставить комментарий →
Пока нет комментариев. Будьте первым.