Перейти к содержимому

Векторы в Rust

Вектор (Vec) похож на массив, но может расти и уменьшаться во время работы программы. В повседневном коде на Rust, когда нужен список чего-то, ты будешь брать Vec гораздо чаще, чем массив фиксированного размера.

fn main() {
let numbers: Vec<i32> = vec![1, 2, 3];
println!("{:?}", numbers);
}

vec![] — это макрос (обрати внимание на !), который собирает Vec из списка значений, почти как литерал массива.

▶ Попробуй сам

Можно начать и с пустого вектора и заполнять его постепенно:

let mut numbers: Vec<i32> = Vec::new();

.push() добавляет элемент в конец. Вектор должен быть mut, ведь ты его меняешь:

fn main() {
let mut numbers = Vec::new();
numbers.push(1);
numbers.push(2);
numbers.push(3);
println!("{:?}", numbers);
}

Вывод:

[1, 2, 3]
▶ Попробуй сам

Те же квадратные скобки, что и у массивов:

fn main() {
let fruits = vec!["яблоко", "банан", "вишня"];
println!("{}", fruits[0]);
println!("Длина: {}", fruits.len());
}
▶ Попробуй сам

Для более безопасного доступа, который не паникует на неверном индексе, используй .get() — он возвращает Option (подробнее — в уроке Option):

fn main() {
let fruits = vec!["яблоко", "банан", "вишня"];
match fruits.get(10) {
Some(fruit) => println!("Нашли: {fruit}"),
None => println!("По этому индексу фрукта нет"),
}
}
▶ Попробуй сам
fn main() {
let mut numbers = vec![1, 2, 3, 4, 5];
numbers.pop(); // удаляет последний элемент -> [1, 2, 3, 4]
numbers.remove(0); // удаляет по индексу -> [2, 3, 4]
println!("{:?}", numbers);
}
▶ Попробуй сам
fn main() {
let numbers = vec![10, 20, 30];
for n in &numbers {
println!("{n}");
}
}
▶ Попробуй сам

Обрати внимание на & перед numbers: цикл for n in &numbers заимствует каждый элемент, а не забирает владение вектором. Если убрать &, вектор переместится в цикл, и после него пользоваться numbers будет нельзя. Почти всегда тебе нужно именно заимствование.

Ты заметил, что тип записан как Vec<i32>: часть <i32> говорит «вектор из значений i32». Такая запись в угловых скобках называется обобщением (generic), и как следует мы разберём её в уроке Обобщения. Пока просто читай Vec<T> как «вектор со значениями типа T».

Начни с пустого вектора. Перебери числа от 1 до 10 и добавляй только чётные. Затем выведи вектор и количество элементов в нём. Подсказка: число n чётное, когда n % 2 == 0.

Показать решение
fn main() {
let mut evens = Vec::new();
for n in 1..=10 {
if n % 2 == 0 {
evens.push(n);
}
}
println!("{:?}", evens);
println!("Нашли чётных чисел: {}", evens.len());
}

Вывод:

[2, 4, 6, 8, 10]
Нашли чётных чисел: 5
▶ Попробуй сам

Заметь, тип мы так и не написали. Rust видит вызовы push(n) и сам понимает, что evens — это Vec<i32>.

Положи в вектор три тарелки: "синяя", "зелёная", потом "красная". Сними две тарелки сверху через .pop() и выведи каждую. В конце выведи, что осталось. Подсказка: .pop() возвращает Option, поэтому выводи его через {:?}.

Показать решение
fn main() {
let mut plates = Vec::new();
plates.push("синяя");
plates.push("зелёная");
plates.push("красная");
println!("Сняли {:?}", plates.pop());
println!("Сняли {:?}", plates.pop());
println!("Осталось: {:?}", plates);
}

Вывод:

Сняли Some("красная")
Сняли Some("зелёная")
Осталось: ["синяя"]
▶ Попробуй сам

pop() всегда берёт последний добавленный элемент — как верхнюю тарелку из стопки. Он возвращает Some(...), а не само значение, потому что вектор может оказаться пустым. Тогда ты получишь None, а не падение программы.

  • Vec<T> — растущий список; создаётся через vec![...] или Vec::new().
  • .push() добавляет в конец, .pop() удаляет последний элемент, .remove(i) удаляет по индексу.
  • vec[i] паникует на неверном индексе, а .get(i) возвращает Option и не паникует.
  • Перебирай через for item in &vec, чтобы заимствовать элементы, а не поглощать вектор.

Проверь себя

1. Что произойдёт, если обратиться к Vec по неверному индексу, например numbers[10]?

2. Какой метод позволяет обратиться по индексу без риска паники?

3. Что делает & в for n in &numbers?

Результат: 0 / 3