Иногда нам нужен список который не зажат в рамки фиксированного размера. В Rust для этого имеется Vec<T> динамический контейнер, который растёт и сжимается по мере необходимости.
Чтобы сделать знакомство с ним более запоминающимся, мы отправимся в путешествие с увлекательным сюжетом.
Главное действующее лицо - капитан космического корабля «Vectoria». Его задача - подготовить корабль к миссии: собрать экипаж, загрузить оборудование, очистить и обслужить системы и наконец отправиться в полёт.
Вместе с капитаном будет робот-помощник RUST-Y или просто Расти. Он умеет писать программы на Rust, но часто совершает типичные ошибки новичка. Капитану придется направлять его и показывать правильные решения.
А ещё в конце статьи вас ждёт испытание: мини-квиз, который покажет насколько хорошо вы справились с миссией и разобрались с Vec.
Готовы? Тогда начнём с самого простого шага: списка экипажа.
Вас вызывают в штаб. Перед стартом новой космической миссии вам поручают задание - предоставить список экипажа. Пока известно только четыре члена команды, ещё двое прибудут позже.
Вы даете задание Расти — составь список экипажа. Ваша задача - научить его вести списки правильно, ведь от этого зависит порядок на борту и успех всей миссии.
Попытка RUST-Y — ошибка новичка
Шаг 1/2
1fnmain(){ 2// Робот записывает имена по отдельности
3letcrew1="Капитан Нова"; 4letcrew2="Инженер Спаркс"; 5letcrew3="Доктор Люмен"; 6letcrew4="Биолог Рост"; 7 8// Он решает: "создам массив"
9letcrew=[crew1,crew2,crew3,crew4];1011// Но сразу лезет к пятому элементу...
12letcrew5=crew[4];1314println!("Пятый член экипажа: {crew5}");15}
Результат: программа упадёт с паникой.
Почему? Потому что массив в Rust имеет фиксированный размер — здесь ровно 4 элемента, а доступ к индексу [4] недопустим.
🔹 Паника — это механизм аварийного завершения, когда нарушено условие безопасности (например, выход за границы памяти).
Результат выполнения:
1fnmain(){2// Список экипажа в гибкой коллекции
3letcrew=vec!["Капитан Нова","Инженер Спаркс","Доктор Люмен","Биолог Рост"];45println!("Экипаж: {:?}",crew);6println!("Длина: {}",crew.len());7println!("Вместимость: {}",crew.capacity());8}
Исправление капитана — Vec
Капитан объясняет:
Расти, массив — это как тетрадь на 4 страницы. Если в экипаже будет больше людей, страниц уже не хватит.
Нам нужен гибкий список, который умеет расти. В Rust для этого есть Vec.
Под капотом он хранит три числа:
указатель (ptr) на блок памяти,
длину (len) — сколько элементов реально в списке,
вместимость (capacity) — сколько ещё можно добавить без расширения памяти.
Результат выполнения:
Запустите код в шаге 2 и вы увидите примерно такой вывод:
Представьте, что у вас есть грузовой отсек корабля.
В нём 4 контейнера — это len.
Отсек рассчитан максимум на 4 контейнера — это capacity.
Если вы добавите ещё один контейнер, корабль “расширит отсек” (реаллокация): выделит новый, более просторный отсек, перенесёт туда все контейнеры и продолжит работу.
push
Добавляет элемент в конец вектора. Если вектора не хватает (len == capacity), он автоматически расширяет память.
len
Возвращает текущее количество элементов в Vec.
capacity
Показывает, сколько элементов можно ещё добавить без перераспределения памяти.
1..=5
Это диапазон (range). В Rust ..= означает “включительно”, то есть от 1 до 5 включительно.
Если бы мы написали 1..5, то получили бы числа 1, 2, 3, 4.
Если мы знаем, что скоро придёт много элементов, выгодно сразу зарезервировать память:
letmutcrew: Vec<String>=Vec::with_capacity(10);
Это создаст пустой вектор, у которого len = 0, но capacity = 10.
На практике чаще всего используют vec![], а остальные способы нужны для оптимизаций или специфических случаев.
Часть 2. Проверка экипажа перед тренировкой (паника и безопасный доступ)#
Перед первой тренировкой капитан просит RUST-Y вызвать члена экипажа по индексу из списка.
Но если робот ошибётся с номером, программа завершится аварийно. Это важный момент: такие сбои недопустимы.
В статье про функциональный Rust мы уже встречались с Option как с типом, который позволяет явно сообщить: “значение может быть, а может не быть” такой аналог null-safety из других языков.
В примере выше Option используется так:
Some(value) — элемент в векторе есть.
None — элемента нет (например, индекс за пределами).
Это хорошее решение языка: вместо “магических значений” или “падений программы” мы получаем явную модель отсутствия данных.
Часть 3. Личные коды экипажа (итерация по вектору и изменение элементов)#
Перед полетом необходимо пройти инструктаж и капитан требует, чтобы каждый член экипажа получил личный код доступа в формате: R-XXX Имя
Например:
R-001 Капитан Нова
R-002 Инженер Спаркс
Робот RUST-Y пробует сделать это с помощью цикла и сразу натыкается на ошибку компилятора.
1fnmain(){ 2letmuttelemetry: Vec<i32>=Vec::new(); 3letmax_len=100; 4 5foriin0..200{ 6telemetry.push(i); 7 8// если размер превысил лимит, убираем старые записи
9iftelemetry.len()>max_len{10telemetry.remove(0);// ❌ неэффективно, сдвигает элементы
11}12}1314println!("Буфер телеметрии: {:?}",telemetry);15}
Исправление капитана — ограниченный буфер
Работает, но remove(0) сдвигает все элементы - на больших объёмах это дорого.