Срезы Vec<T> в Rust: как получить и не сломать голову в 2025

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Срезы Vec<T> в Rust: как это работает и зачем это нужно?

Если вы когда-нибудь писали на Rust и задавались вопросом "How do I get a slice of a Vec<T> in Rust?" — вы не одиноки. Это один из самых популярных вопросов на Stack Overflow, набравший тысячи просмотров. И неспроста: работа с векторами и их срезами — фундаментальный навык, без которого невозможно представить ни один серьёзный проект на Rust.

В этой статье мы не просто покажем, как получить срез. Мы разберём лучшие практики 2025 года, научимся избегать типичных ловушек и поймём, почему Rust вообще заставляет нас так много думать о заимствованиях. Поехали!

Что такое срез (slice) в Rust?

Прежде чем брать срез, давайте разберёмся, что это такое. Срез — это ссылка на непрерывную последовательность элементов в памяти. В Rust срез обозначается как &[T]. Это не владеющая структура данных — она просто «смотрит» на часть данных, принадлежащих кому-то другому (например, вектору или массиву).

Главное преимущество срезов — безопасность. Компилятор Rust гарантирует, что вы не выйдете за границы памяти, а система заимствования не позволит вам изменить данные, пока на них есть активные ссылки.

Почему не использовать вектор напрямую?

Вектор (Vec<T>) — это владеющая структура. Если вы передаёте его в функцию, вы либо перемещаете его (теряя доступ), либо передаёте ссылку. Срезы дают вам гибкость: вы можете передать только часть данных, не копируя их и не теряя контроль над исходным вектором.

Например, вы работаете с большим массивом данных и хотите передать его фрагмент в функцию для анализа. Срез — идеальный инструмент: он лёгкий (всего два слова: указатель и длина) и безопасный.

Основные способы получить срез Vec<T>

Давайте перейдём к практике. Есть несколько способов получить срез из вектора. Мы рассмотрим самые популярные и удобные.

1. Оператор индексирования: &vec[start..end]

Самый прямолинейный способ — использовать оператор диапазона ..:

let vec = vec![1, 2, 3, 4, 5];
let slice: &[i32] = &vec[1..4]; // срез от индекса 1 до 4 (не включая 4)
println!("{:?}", slice); // [2, 3, 4]

Этот метод использует рантайм-проверку границ. Если вы выйдете за пределы вектора, программа запаникует (panic). Для учебных примеров это нормально, но в production-коде стоит быть осторожнее.

Вы можете использовать различные формы диапазонов:

  • &vec[..] — весь вектор
  • &vec[2..] — от индекса 2 до конца
  • &vec[..3] — от начала до индекса 3 (не включая)
  • &vec[1..=3] — от 1 до 3 включительно

2. Метод .as_slice()

Этот метод возвращает срез на весь вектор. Он безопаснее, чем &vec[..], потому что явно выражает намерение:

let vec = vec![10, 20, 30];
let slice: &[i32] = vec.as_slice();
println!("{:?}", slice); // [10, 20, 30]

Метод as_slice() особенно полезен, когда вы хотите подчеркнуть, что работаете со срезом, а не с вектором. Это делает код более читаемым и самодокументируемым.

3. Получение изменяемого среза: as_mut_slice()

Если вам нужно изменить элементы внутри среза (но не сам вектор), используйте as_mut_slice():

let mut vec = vec![1, 2, 3];
let slice: &mut [i32] = vec.as_mut_slice();
slice[0] = 100;
println!("{:?}", vec); // [100, 2, 3]

Обратите внимание: вектор должен быть объявлен как mut. Также вы не можете одновременно иметь неизменяемый и изменяемый срезы — это нарушит правила заимствования.

Как получить подмассив (sub-slice) без паники?

В production-коде паника из-за выхода за границы — это плохо. Rust предоставляет безопасные альтернативы:

Метод .get()

Возвращает Option<&[T]>:

let vec = vec![1, 2, 3, 4, 5];
let slice = vec.get(1..4);
match slice {
    Some(s) => println!("{:?}", s), // [2, 3, 4]
    None => println!("Индекс вне диапазона"),
}

Это идеальный выбор, когда вы не уверены в корректности индексов.

Метод .get_unchecked()

Если вы абсолютно уверены, что индексы корректны, можно использовать get_unchecked(). Он не проверяет границы и может привести к неопределённому поведению при ошибке. Используйте только в критических участках кода, где производительность важнее безопасности, и только после тщательной проверки.

let vec = vec![1, 2, 3];
// Без проверки границ — используйте с осторожностью!
let slice = unsafe { vec.get_unchecked(0..2) };
println!("{:?}", slice); // [1, 2]

Срезы и заимствования: типичные ошибки

Самая частая ошибка новичков — попытка изменить вектор, пока на него есть активный срез. Rust это запрещает:

let mut vec = vec![1, 2, 3];
let slice = &vec[..];
vec.push(4); // Ошибка! Нельзя изменять vec, пока slice заимствован
println!("{:?}", slice);

Компилятор скажет что-то вроде "cannot borrow `vec` as mutable because it is also borrowed as immutable". Это не баг, а фича: Rust защищает вас от ошибок, которые могли бы привести к висячим указателям.

Решение: либо завершите работу со срезом, прежде чем изменять вектор, либо используйте изменяемый срез (&mut [T]), если это возможно.

Лучшие практики 2025 года

Rust развивается, и вместе с ним меняются лучшие практики. Вот что стоит знать и использовать в 2025 году:

1. Используйте as_slice() для явности

Хотя &vec[..] работает, vec.as_slice() читается лучше и сразу говорит о намерении. Это особенно важно

Блоги

Рекомендаций по книгам