Тип Slice представляет часть набора элементов, например, часть массива.
Формальное определение slice:
let slice = &набор_элементов[начальный_индекс..конечный_индекс];
Для извлечения набора элементов применяется синтаксис последовательностей - оператор .., слева от которого указывается начальный индекс, а справа - конечный индекс.
При этом слайс определяется как ссылка, поэтому он не владеет полученным поднабором элементов, а лишь содержит ссылку на этот поднабор.
Например, определим slice, который представляет часть массива:
fn main(){
let numbers = [1, 2, 3, 4, 5, 6, 7, 8];
let slice = &numbers[1..5]; // получим слайс
println!("{:?}", slice); // [2, 3, 4, 5]
}
Выражение numbers[1..5] указывает, что мы хотим взять из массива элементы, начиная с 1-го индекса по 5-й.
Так как индексация в массиве начинается с нуля, а конечный индекс в результат не включается, то из массива [1, 2, 3, 4, 5, 6, 7, 8]
мы получим подмассив [2, 3, 4, 5].
Стоит отметить, что тип слайса обычно представляет тип &[T], где T - тип данных в слайсе. Так, мы можем в предыдущем примере явным
образом указать тип:
fn main(){
let numbers = [1, 2, 3, 4, 5, 6, 7, 8];
let slice: &[i32]; // объвляем слайс
slice = &numbers[1..5]; // получим слайс из вектора
println!("{:?}", slice); // [2, 3, 4, 5]
}
Слайс может выступать в качестве параметра функции:
fn print_slice(slice: &[i32]){
println!("{:?}", slice);
}
fn main(){
let numbers = [1, 2, 3, 4, 5, 6, 7, 8];
let nums = &numbers[1..5]; // получим слайс из вектора
print_slice(nums);
}
Также слайс можно возвращать из функции:
fn create_slice(data: &Vec<i32>, start: usize, end: usize) -> &[i32]{
if start < end && end < data.len(){
&data[start..end]
}
else{
&data[0..data.len()-1]
}
}
fn main(){
let numbers =vec![1, 2, 3, 4, 5, 6, 7, 8];
let nums1 = create_slice(&numbers, 1, 5);
println!("{:?}", nums1); // [2, 3, 4, 5]
let nums2 = create_slice(&numbers, 2, 4);
println!("{:?}", nums2); // [3, 4]
}
После инициализации слайса можно обращаться к его элементам. Например, как в массиве по индексу:
println!("slice[0]={}", slice[0]); // выведем первый элемент слайса
Также можно перебрать элементы в цикле for. Например:
fn main(){
let numbers = [1, 2, 3, 4, 5, 6, 7, 8];
let slice = &numbers[1..5]; // получим слайс
println!("slice[0]={}", slice[0]); // выведем первый элемент слайса
// перебор слайса
for num in slice{
print!("{} ", num) // 2 3 4 5
}
}
Консольный вывод программы:
slice[0]=2 2 3 4 5
Если надо взять элементы, начиная с индекса 0, то его можно не указывать. Также можно не указывать конечный индекс, если необходимо взять элементы вплоть до последнего. Таким образом, можно применять следующие комбинации:
numbers[..] эквивалентно [1, 2, 3, 4, 5, 6, 7, 8]
numbers[..3] эквивалентно [1, 2, 3 ]
numbers[..=3] эквивалентно [1, 2, 3, 4]
numbers[1..] эквивалентно [2, 3, 4, 5, 6, 7, 8]
numbers[1..3] эквивалентно [2, 3]
numbers[1..=3] эквивалентно [2, 3, 4]
Также через слайс можно изменять элементы поднабора (и соответственно начального набора). В этом случае слайс должен быть определен как изменяемая ссылка с оператором &mut:
fn main(){
let mut numbers = [1, 2, 3, 4, 5, 6, 7, 8];
let slice = &mut numbers[1..5]; // получим слайс
// изменим первый элемент слайса
slice[0] = 81;
println!("slice[0]={}", slice[0]); // slice[0]=81
// перебор измененного слайса
for num in slice{
print!("{} ", num) // 81 3 4 5
}
println!();
// перебор начального массива
for num in numbers{
print!("{} ", num) // 1 81 3 4 5 6 7 8
}
}
Подобным образом можно получить поднабор и из других наборов элементов, например, из строки - типа String:
fn main(){
let message = "hello world!";
let slice = &message[..5]; // часть с строки с начала до 6 символа
println!("slice={}", slice); // hello
println!("message={}", message); // hello world!
}
Стоит отметить, что для представления строкового слайса в Rust есть специальный тип - &str, который уже ранее использовался в предыдущих статьях. То есть в данном случае мы могли бы написать:
fn main(){
let message = "hello world!";
let slice: &str = &message[..5]; // часть с строки с начала до 6 символа
println!("slice={}", slice); // hello
println!("message={}", message); // hello world!
}
Поэтому когда мы присваивали переменной обычную строку:
let hello = "hello world";
Фактически здесь переменная hello представляла строковый слайс или тип &str.