Найти в Дзене
Один Rust не п...Rust

Слайсы в Rust

t.me/oneRustnoqRust Для чего нужна данная статья? : Научиться заменять циклы слайсом используя итераторы, работать с любыми непрерывными данными, безопасно манипулировать памятью и использовать преимущества статической типизации. Зачем Вам это уметь? : Слайсы в Rust часто используются как ссылки на массив или вектор. Они неизменяемые или изменяемые: Неизменяемый слайс (&[T]): let array = [1, 2, 3, 4]; let slice: &[i32] = &array[1..3]; println!("{:?}", slice); // [2, 3] Изменяемый слайс (&mut [T]): let mut array = [1, 2, 3, 4]; let slice: &mut [i32] = &mut array[1..3]; slice[0] = 5; println!("{:?}", array); // [1, 5, 3, 4] Вы можете перебирать элементы слайса с помощью итераторов: fn main() { let array = [10, 20, 30, 40, 50]; let slice: &[i32] = &array[1..4]; for (i, value) in slice.iter().enumerate() { println!("Элемент {}: {}", i, value); // Элемент 0: 20, Элемент 1: 30, Элемент 2: 40 } } С помощью итератора можно модифицировать элементы изменяемого слайса: fn main() { let mut arra
Оглавление
ML на RUST без заморочек

t.me/oneRustnoqRust

Для чего нужна данная статья? :

Научиться заменять циклы слайсом используя итераторы, работать с любыми непрерывными данными, безопасно манипулировать памятью и использовать преимущества статической типизации.

Зачем Вам это уметь? :

1. Ссылочные слайсы (&[T] и &mut [T])

Слайсы в Rust часто используются как ссылки на массив или вектор. Они неизменяемые или изменяемые:

Неизменяемый слайс (&[T]):

let array = [1, 2, 3, 4];

let slice: &[i32] = &array[1..3];

println!("{:?}", slice); // [2, 3]

Изменяемый слайс (&mut [T]):

let mut array = [1, 2, 3, 4];

let slice: &mut [i32] = &mut array[1..3];

slice[0] = 5;

println!("{:?}", array); // [1, 5, 3, 4]

Вы можете перебирать элементы слайса с помощью итераторов:

fn main() {

let array = [10, 20, 30, 40, 50];

let slice: &[i32] = &array[1..4];

for (i, value) in slice.iter().enumerate() {

println!("Элемент {}: {}", i, value); // Элемент 0: 20, Элемент 1: 30, Элемент 2: 40

}

}

С помощью итератора можно модифицировать элементы изменяемого слайса:

fn main() {

let mut array = [1, 2, 3, 4, 5];

let slice: &mut [i32] = &mut array[1..4];

for value in slice.iter_mut() {

*value *= 2; // Удваиваем значение каждого элемента

}

println!("{:?}", array); // [1, 4, 6, 8, 5]

}

2. Слайсы строк (&str)

Слайс строки (&str) — это специальный вид неизменяемого слайса, который указывает на последовательность символов UTF-8:

let text = "Hello, Rust!";

let slice: &str = &text[7..11];

println!("{}", slice); // Rust

Срезы строк работают с байтами, поэтому использование некорректных границ может вызвать ошибку. Например:

fn main() {

let text = "Привет"; // "Привет" в UTF-8 занимает больше байтов

// Ошибка выполнения: границы среза не совпадают с границами символов UTF-8

let slice = &text[0..3];

println!("{}", slice);

}

Для безопасной работы используйте методы, учитывающие символы:

fn main() {

let text = "Привет";

let slice = &text[..4]; // Берем первые 4 символа (включая UTF-8)

println!("{}", slice); // Прив

}

Можно использовать метод .split() для получения срезов строк:

fn main() {

let text = "Hello Rust World";

for word in text.split_whitespace() {

println!("{}", word);

}

// Вывод:

// Hello

// Rust

// World

}

С векторами удобно работать через .as_slice() или .as_mut_slice():

fn main() {

let mut vec = vec![10, 20, 30, 40];

let slice = vec.as_slice();

println!("{:?}", slice); // [10, 20, 30, 40]

let mut_slice = vec.as_mut_slice();

mut_slice[0] = 50; // Изменяем первый элемент

println!("{:?}", vec); // [50, 20, 30, 40]

}

3. Динамические массивы (Vec<T>)

Хотя Vec<T> не является слайсом, он предоставляет метод .as_slice(), чтобы получить ссылочный слайс:

let vec = vec![10, 20, 30, 40];

let slice: &[i32] = &vec[1..3];

println!("{:?}", slice); // [20, 30]

4. Слайсы с произвольной длиной (Box<[T]>)

Вы можете использовать Box<[T]> для создания слайсов, выделенных в куче. Это полезно для работы с динамическими массивами:

let boxed_slice: Box<[i32]> = vec![1, 2, 3, 4].into_boxed_slice();

println!("{:?}", boxed_slice);

Box<[T]> позволяет передавать владение массивом с произвольной длиной:

fn main() {

let boxed_slice: Box<[i32]> = vec![1, 2, 3, 4].into_boxed_slice();

println!("{:?}", boxed_slice); // [1, 2, 3, 4]

}

5. Создание слайсов через std::slice

Модуль std::slice предоставляет функции для создания слайсов вручную:

Создание слайса из сырого указателя:

use std::slice;

let data = [1, 2, 3, 4];

let ptr = data.as_ptr();

let slice: &[i32] = unsafe { slice::from_raw_parts(ptr, 4) };

println!("{:?}", slice); // [1, 2, 3, 4]

Из изменяемого указателя:

let mut data = [5, 6, 7, 8];

let ptr = data.as_mut_ptr();

let slice: &mut [i32] = unsafe { slice::from_raw_parts_mut(ptr, 4) };

slice[0] = 10;

println!("{:?}", data); // [10, 6, 7, 8]

Когда мы работаем с указателями, важно помнить об unsafe блоках:

use std::slice;

fn main() {

let data = [10, 20, 30, 40];

let ptr = data.as_ptr();

let slice = unsafe { slice::from_raw_parts(ptr, 4) };

println!("{:?}", slice); // [10, 20, 30, 40]

}

6. Слайсы через массивы фиксированной длины

Слайсы можно получить из массивов фиксированной длины:

let array: [i32; 4] = [1, 2, 3, 4];

let slice: &[i32] = &array[..];

println!("{:?}", slice); // [1, 2, 3, 4]

7. Динамические срезы в других структурах (например, Cow<[T]>)

Слайсы могут быть использованы в Cow (Copy-On-Write), который позволяет оптимизировать копирование данных:

use std::borrow::Cow;

let slice: &[i32] = &[1, 2, 3];

let cow: Cow<[i32]> = Cow::Borrowed(slice);

println!("{:?}", cow); // [1, 2, 3]

8. Типовые параметры и обобщённые слайсы

Слайсы могут использоваться в функциях с обобщёнными типами:

fn print_slice<T: std::fmt::Debug>(slice: &[T]) {

println!("{:?}", slice);

}

let array = [1, 2, 3, 4];

print_slice(&array[1..3]); // [2, 3]

Слайсы идеально подходят для функций, работающих с любыми типами:

fn sum_elements<T: std::ops::Add<Output = T> + Copy>(slice: &[T]) -> T {

slice.iter().copied().fold(T::default(), |acc, x| acc + x)

}

fn main() {

let array = [1, 2, 3, 4];

let result = sum_elements(&array);

println!("Сумма: {}", result); // Сумма: 10

}

9. Пользовательские реализации через Deref

Можно создать свои структуры, которые ведут себя как слайсы, реализовав трейт Deref:

use std::ops::Deref;

struct MySlice {

data: Vec<i32>,

}

impl Deref for MySlice {

type Target = [i32];

fn deref(&self) -> &Self::Target {

&self.data

}

}

let my_slice = MySlice { data: vec![1, 2, 3] };

println!("{:?}", &*my_slice); // [1, 2, 3]

Если вы создаете структуру данных, которая должна вести себя как слайс, используйте Deref:

use std::ops::Deref;

struct MySlice {

data: Vec<i32>,

}

impl Deref for MySlice {

type Target = [i32];

fn deref(&self) -> &Self::Target {

&self.data

}

}

fn main() {

let my_slice = MySlice { data: vec![1, 2, 3] };

println!("{:?}", &*my_slice); // [1, 2, 3]

}

10. Слайсы через Arc<[T]> или Rc<[T]>

Для управления памятью с использованием ссылочного счётчика можно применять Arc<[T]> или Rc<[T]>:

use std::sync::Arc;

let arc_slice: Arc<[i32]> = vec![1, 2, 3].into();

println!("{:?}", arc_slice); // [1, 2, 3]

Для многопоточных программ удобно использовать Arc<[T]>:

use std::sync::Arc;

fn main() {

let arc_slice: Arc<[i32]> = vec![1, 2, 3].into();

let arc_clone = arc_slice.clone();

println!("{:?}", arc_slice); // [1, 2, 3]

println!("{:?}", arc_clone); // [1, 2, 3]

}