Массивы представляют набор элементов, при этом все элементы набора должны представлять один и тот же тип данных.
В Rust массив можно определять различными способами. Простейшее формальное объявление массива:
let название_массива: [тип_данных; размер];
После названия массива через двоеточие, как и в общем случае, указывается тип данных. Но в случае с массивом тип данных массива указывается в квадратных скобках и состоит из двух компонент: сначала указывается тип данных элементов массива, а потом через точку с запятой размер массива, то есть число его элементов.
Например, объявление массива, который имеет 7 элементов типа i32:
let numbers: [i32; 7];
Фактически выражение [i32; 7] выступает в качестве типа массива. То есть фактически мы определили переменную numbers и
указали ее тип - [i32; 7] - массив из 7 элементов типа i32. Но такая переменная еще неинициализирована. Поэтому использовать
мы ее не можем.
Для инициализации переменной массива необходимо ей присвоить набор значений. Простейшее определение с инициализацией:
let переменная_массива: [тип_данных; размер] = [элемент1, элемент2, ... элементN];
В качестве значения в квадратных скобках через запятую перечисляются элементы массива. Например:
let numbers: [i32; 7] = [1, 2, 3, 5, 8, 13, 21];
В данном случае массив numbers имеет 7 элементов: 1, 2, 3, 5, 8, 13, 21.
Как и в случае с обычной переменной можно сначала объявить переменную, а затем инициализировать ее:
let numbers: [i32; 7]; numbers = [1, 2, 3, 5, 8, 13, 21];
Также если массив инициализируется при его определении, то можно не указывать тип массива:
let numbers = [1, 2, 3, 5, 8, 13, 21];
В этом случае Rust сам выведет тип - [i32; 7]
Rust предоставляет удобный способ для вывода всего массива на консоль:
fn main(){
let numbers = [1, 2, 3, 4, 5, 6, 7, 8];
println!("{:?}", numbers); // [1, 2, 3, 4, 5, 6, 7, 8]
}
Для обращения к элементам массива используется их числовой индекс:
название_массива[индекс_элемента]
Каждый элемент массива имеет числовой индекс. Индексация начинается с нуля. То есть первый элемент массива будет иметь индекс 0, второй элемент - индекс 1 и так далее.
Например, выведем на консоль первый и третий элементы массива:
fn main()
{
let users = ["Tom", "Bob", "Sam"];
println!("{}", users[0]); // Tom
println!("{}", users[2]); // Sam
}
Подобным образом можно не только получить, но и изменять элемент массива. Однако, если мы собираемся изменять элементы массива, то он должен определен с модификатором mut:
fn main(){
let mut users = ["Tom", "Bob", "Sam"];
// выводим второй элемент массива
println!("{}", users[1]); // Bob
// изменяем второй элемент массива
users[1] = "Bill";
// снова выводим второй элемент массива
println!("{}", users[1]); // Bill
}
Стоит учитывать, что мы не можем обратиться к несуществующему элементу. Например, выше в примере в массиве есть всего три элемента. Соответственно индексом последнего элемента является число 2. Поэтому к элементам с индексами 3, 4 и больше мы обратиться не можем, так как их просто не существует. Например, в следующем случае при попытке обращения к несуществующему элементу мы столкнемся с ошибкой на этапе компиляции:
fn main(){
let mut users = ["Tom", "Bob", "Sam"];
users[6] = "Bill"; // !Ошибка - элемента с индексом 6 в массиве users нет
}
Также следует учитывать, что массивы в Rust имеют фиксированный размер. После их определения мы не можем ни уменьшить, ни увеличить их размер.
С помощью метода len() можно получить длину массива:
fn main(){
let users = ["Tom", "Bob", "Sam"];
println!("count: {}", users.len()); // count: 3
}
Для перебра массива применяется цикл for:
fn main(){
let users = ["Tom", "Bob", "Sam"];
for user in users {
print!("{} ", user);
}
println!(); // переходим на следующую строку в консоли
let numbers = [1, 2, 3, 5, 8, 13, 21];
for n in numbers{
print!("{} ", n);
}
}
Консольный вывод программы:
Tom Bob Sam 1 2 3 5 8 13 21
Используя длину массива, можно перебрать массив с учетом индексов элементов:
fn main(){
let users = ["Tom", "Bob", "Sam"];
for i in 0..users.len(){
println!("{}", users[i]);
}
}
В данном случае создается диапазон чисел от 0 до users.len(), каждое из которых помещается в переменную i, выполняющую роль индекса.
Массивы также имеют еще одну форму определения:
let название_массива: [тип_данных; размер] = [значение_по умолчанию; размер];
В качестве значения в квадратных скобках сначала указывается значение по умолчанию, которым заполняется массив. А затем через точку с запятой количество элементов массива, которые должны принять это значение.
let numbers: [i32; 5] = [2; 5];
В данном случае каждый из пяти элементов массива numbers будет иметь значение "2".
Также можно сократь определение массива и не указывать тип:
fn main(){
let numbers = [2; 5];
for n in numbers{
print!("{} ", n); // 2 2 2 2 2
}
}
Для массивов доступен ряд методов. Все они включены в модуль std::array. Рассмотрим применение наиболее используемого метода - sort(), который сортирует массив:
fn main(){
let mut users = ["Tom", "Bob", "Sam"];
users.sort();
println!("users: {:?} ", users);
let mut numbers = [1, 8, 5, 21, 13, 2, 3];
numbers.sort();
println!("numbers: {:?} ", numbers);
}
По умолчанию массивы сортируются по возрастанию (строки сортируются в лексикографическом порядке), причем подобные массивы должны быть изменяемыми. Консольный вывод:
users: ["Bob", "Sam", "Tom"] numbers: [1, 2, 3, 5, 8, 13, 21]
Массивы Rust реализуют трейт Copy, если их типы элементов также реализуют Copy. Это означает, что для подобных массивов копирование происходит неявно во время присваиваний или вызовов функций:
fn main(){
let original = [1, 2, 3];
let copied = original;
println!("copied: {:?} ", copied); // copied: [1, 2, 3]
}
Однако если тип элемента массива не реализует трейт Copy, то подобный массив нельзя скопировать напрямую. Вместо этого нужно использовать метод to_owned() для клонирования массива. Например:
fn main(){
let original = ["Tom", "Bob", "Sam"];
let copied = original.to_owned();
println!("copied: {:?} ", copied); // copied: ["Tom", "Bob", "Sam"];
}
Стоит отметить, что клонирование массива предполагает создание нового массива с идентичными элементами, что может оказаться затратно с точки зрения памяти и производительности.
Массивы могут быть одномерными и многомерными. Одномерные массивы можно представить в виде строки или столбца, в которых элементы массивы расположены в ряд. Ранее рассмотренные массивы все были одномерными, например:
let numbers = [1, 2, 3, 4, 5, 6];
Массив numbers является одномерным, его можно представить следующим образом:
| 1 | 2 | 3 | 4 | 5 | 6 |
Многомерные массивы же представляют массивы, элементы которых сами являются массивами, то есть массивы содержат другие массивы. Рассмотрим частый случай многомерного массива - двухмерный массив:
let numbers = [
[1, 2, 3],
[4, 5, 6]
];
Здесь массив numbers содержит два элемента, которые, в свою очередь, также являются массивами. Причем каждый такой вложенный массив имеет три элемента. Типом
такого массива является [[i32; 3]; 2]. То есть мы можем записать следующим образом:
let numbers: [[i32; 3]; 2] = [
[1, 2, 3],
[4, 5, 6]
];
В данном случае тип массива [[i32; 3]; 2] указывает, что массив имеет два элемента типа [i32; 3].
Тип же [i32; 3] представляет массив из 3 чисел типа i32
Такой массив еще можно представить в виде таблицы, где каждый вложенный массив представляет строку
| 1 | 2 | 3 |
| 4 | 5 | 6 |
Для обращения к элементам подобного массива также используются индексы:
fn main(){
let numbers: [[i32; 3]; 2] = [
[1, 2, 3],
[4, 5, 6]
];
let array0: [i32;3] = numbers[0]; // первый вложенный массив
println!("{:?}", array0); // [1, 2, 3]
let array1: [i32;3] = numbers[1]; // первый вложенный массив
println!("{:?}", array1); // [4, 5, 6]
}
Здесь выражение numbers[0] указывает, что мы обращаемся к первому элементу массива numbers (элементу с индексом 0), то есть к подмассиву [1, 2, 3].
Поскольку каждый элемент сам является массивом, то для его вывода используем макрос println!("{:?}", array0)
Для получения элемента во вложенном массиве, можно использовать два индекса:
fn main(){
let numbers: [[i32; 3]; 2] = [
[1, 2, 3],
[4, 5, 6]
];
let n1: i32 = numbers[0][1]; // второй элемент первого вложенного массива
println!("{}", n1); // 2
}
В выражении numbers[0][1] первый индекс [0] указывает, что мы получаем первый вложенный массив, а второй индекс [1] -
что мы получаем второе число в этом вложенном массиве. То есть в итоге это будет число 2.
Для перебора многомерного массива можно использовать все те же способы. Например:
fn main(){
let numbers: [[i32; 3]; 2] = [
[1, 2, 3],
[4, 5, 6]
];
for array in numbers{
println!("{:?}", array);
}
}
Здесь мы пробегаемся по всем элементам массива numbers. Консольный вывод:
[1, 2, 3] [4, 5, 6]
В данном случае переменная array представляет вложенный массив. А чтобы обратиться к вложенным элементам внутри этого массива array, мы можем использовать вложенный цикл:
fn main(){
let numbers: [[i32; 3]; 2] = [
[1, 2, 3],
[4, 5, 6]
];
for array in numbers{ // получаем вложенные массивы
for number in array{ // получаем числа во вложенных массивах
print!("{} ", number);
}
println!();
}
}
Консольный вывод:
1 2 3 4 5 6