Как получить срез (slice) из Vec<T> в Rust: полное руководство
Понимание срезов в Rust
Срез (slice) — это представление непрерывной последовательности элементов, которое не владеет данными. В Rust срезы всегда являются ссылками: &[T] для неизменяемого среза и &mut [T] для изменяемого. Это ключевой момент: срез — это не копия данных, а "окно" в существующий массив или вектор.
1. Получение неизменяемого среза
Ваша ошибка в том, что вы пытаетесь получить срез напрямую, без взятия ссылки. Правильный синтаксис — взять ссылку на диапазон:
fn main() {
let numbers = vec![10, 20, 30, 40, 50, 60];
// Правильно: берём ссылку на диапазон
let slice: &[i32] = &numbers[1..4]; // [20, 30, 40]
println!("{:?}", slice);
}
Оператор & перед диапазоном говорит компилятору: "дай мне ссылку на подмассив". Диапазон 1..4 включает элементы с индексами 1, 2, 3 (исключая 4).
2. Получение изменяемого среза
Для изменяемого среза вектор должен быть объявлен как mut:
fn main() {
let mut numbers = vec![10, 20, 30, 40, 50, 60];
let slice: &mut [i32] = &mut numbers[1..4];
slice[0] = 99; // Меняем 20 на 99
println!("{:?}", numbers); // [10, 99, 30, 40, 50, 60]
}
Важно: вы не можете одновременно иметь неизменяемый и изменяемый срезы на один и тот же вектор — это нарушило бы правила владения Rust.
3. Разница между &numbers[..] и numbers.as_slice()
Оба способа дают одинаковый результат — полный срез вектора:
fn main() {
let numbers = vec![1, 2, 3];
let slice1: &[i32] = &numbers[..]; // Полный диапазон
let slice2: &[i32] = numbers.as_slice(); // Метод вектора
assert_eq!(slice1, slice2); // Оба равны
}
numbers.as_slice() — это явный вызов метода, который делает намерения более читаемыми. &numbers[..] — это идиоматичный синтаксис, использующий перегрузку оператора []. Выбирайте тот, который кажется вам понятнее.
4. Создание нового вектора из среза
Для создания нового вектора из среза используйте метод to_vec() или Vec::from():
fn main() {
let numbers = vec![10, 20, 30, 40, 50];
let slice = &numbers[1..4]; // [20, 30, 40]
// Способ 1: to_vec()
let new_vec1: Vec<i32> = slice.to_vec();
// Способ 2: Vec::from()
let new_vec2 = Vec::from(slice);
// Способ 3: iter().collect()
let new_vec3: Vec<i32> = slice.iter().cloned().collect();
println!("{:?}", new_vec1); // [20, 30, 40]
}
Обратите внимание: to_vec() копирует элементы, поэтому исходный вектор остаётся неизменным. Для типов, реализующих трейт Copy (например, i32), это происходит автоматически. Для сложных типов потребуется реализация Clone.
Практический совет
Срезы — эффективный способ работы с подмножествами данных без лишних аллокаций. Используйте их в функциях, которым не нужно владение:
fn sum_slice(data: &[i32]) -> i32 {
data.iter().sum()
}
fn main() {
let v = vec![1, 2, 3, 4, 5];
let result = sum_slice(&v[1..4]); // 2+3+4 = 9
println!("{}", result);
}
Это идиоматично и безопасно благодаря проверкам границ во время выполнения.
<<Альтернативный подход: использование итераторов
Если вам не нужен именно срез как структура данных, а требуется только обработать подмножество элементов, можно использовать итераторы с методами skip() и take(). Это иногда удобнее, особенно для цепочек преобразований.
fn main() {
let numbers = vec![10, 20, 30, 40, 50, 60];
// Получаем элементы с индексами 1..4 через итератор
let subset: Vec<&i32> = numbers.iter().skip(1).take(3).collect();
println!("{:?}", subset); // [20, 30, 40]
// Или сразу собираем в новый вектор со значениями
let subset_owned: Vec<i32> = numbers.iter()
.skip(1)
.take(3)
.cloned()
.collect();
println!("{:?}", subset_owned); // [20, 30, 40]
}
Преимущество этого подхода в том, что вы можете легко комбинировать фильтрацию, преобразования и другие операции, не создавая промежуточный срез. Например, можно отфильтровать только чётные числа из диапазона:
let evens: Vec<i32> = numbers.iter()
.skip(1)
.take(4)
.filter(|&&x| x % 2 == 0)
.cloned()
.collect();
println!("{:?}", evens); // [20, 40]
Однако помните, что итераторы не дают прямого доступа по индексу, как срез. Если вам нужен произвольный доступ к элементам (например, slice[2]), лучше использовать срез. Выбор зависит от задачи: срез для прямого индексирования, итератор для последовательной обработки.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги