Векторы

Первым видом коллекции, который мы разберем будет вектор Vec<T>. Вектора могут сохранять множество данных в одной структуре, сохраняя данные один за одним. Данные могут быть только одного типа. Этот тип данных удобен, когда нужно иметь список данных.

Создание нового вектора

Для создания нового вектора используется функция Vec::new:

fn main() {
    let v: Vec<i32> = Vec::new();
    print!("{:?} ", v);


}

Обратите внимание, что мы добавили описание (аннотацию) типа данных. Очень важным момент: пока мы не добавим хотя бы один элемент в вектор, компилятор (Rust) не будет знать, что за тип данных будет содержать в этой коллекции. Как мы уже говорили, вектор может содержать только один тип данных. Это его особенность.

Более удобный способ инициализации вектора - с помощью макроса vec! (по умолчанию тип числовых данных i32):

fn main() {
   let v = vec![1, 2, 3];
   println!("{:?}",v);
}

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

fn main() {
   let v = vec![1, 2, (3 as u64)];
   println!("{:?}",v);
}

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

Изменение вектора

Для заполнения вектора данными, используется метод push:

fn main() {
   //let mut v = Vec::new();
   let mut v = vec![];
    v.push(5);
    v.push(6);
    v.push(7);
    v.push(8);
    println!("{:?}",v);
}

Обратите внимание, что мы использовали mut для того, чтобы была возможность изменять значения переменной. Компилятор определяет тип данных после добавления первого элемента в коллекцию.

Удаление элементов из вектора

По работе памяти с объектами вектор ведёт себя также как и структура. Память, занимаемая вектором очищается после выхода кода за пределы области видимости:


# #![allow(unused_variables)]
#fn main() {
{
    let v = vec![1, 2, 3, 4];

    // do stuff with v

} // <- v goes out of scope and is freed here
#}

При удалении коллекции, удаляется всё её содержимое. Есть кое-какие особенности в этом процессе, которые мы обсудим далее.

Чтение данных вектора

Следующим навыком, который вам пригодится при работе с векторами - это чтение содержания. Существует два способа получения ссылке на данные: по индексу и с помощью метода get:

fn main() {
   let v = vec![1, 2, 3, 4, 5];

    let third: &i32 = &v[2];
    println!("{}", third);
    let third: Option<&i32> = v.get(2);
    println!("{:?}", third);
}

Содержимое вектора индексируется по номерам начиная с 0. Второй способ - использовать метод get, который возвращает Option<&T>. Каждый из этих способов имеет свои плюсы и минусы. Плюсы. Первый быстрый, второй надёжный (при ошибке выбора индекса программа, аварийно, прекращает работу, а при втором просто возвращает None). Какой способ доступа к данным выбрать зависит он контекста и целей программы.


# #![allow(unused_variables)]
#fn main() {
let v = vec![1, 2, 3, 4, 5];

let does_not_exist = &v[100];
let does_not_exist = v.get(100);
#}
fn main() {
    let v = vec![1, 2, 3, 4, 5];
    println!("{}", v[100]);
    //println!("{}", &v[100]);
}
thread 'main' panicked at 'index out of bounds: the len is 5 but the index is 100', /checkout/src/liballoc/vec.rs:1555:10
note: Run with `RUST_BACKTRACE=1` for a backtrace.
fn main() {
    let v = vec![1, 2, 3, 4, 5];
    println!("{}", v[100]);
}
None

Неправильные ссылки

Освежите в памяти правила заимствования, ссылочной целостности, с которыми мы познакомились в главе 4! Здесь мы ещё раз посмотрим на их работу в действии в контексте работы с векторами:

let mut v = vec![1, 2, 3, 4, 5];

let first = &v[0];

v.push(6);

Получим ошибку компиляции:

error[E0502]: cannot borrow `v` as mutable because it is also borrowed as
immutable
  |
4 | let first = &v[0];
  |              - immutable borrow occurs here
5 |
6 | v.push(6);
  | ^ mutable borrow occurs here
7 | }
  | - immutable borrow ends here

Правильный код будет иметь вид:

fn main() {
    let mut v = vec![1, 2, 3, 4, 5];
    v.push(6);
    let first = &v[0];

    //v.push(6);

    println!("{:?}", v);
    println!("{}", first);
}

Такие строгие ограничения существуют для того, чтобы исключить ошибку

Более подробно об этом читайте на странице https://doc.rust-lang.org/stable/nomicon/vec.html.

Использование перечисления для хранения множества разных типов

В начале этой главы мы выяснили, что вектор может хранить только однотипные данные. Бывают ситуации, когда нужно хранить разные типы данных. В этом нм помогу перечисления.

Приведем пример. К примеру, нам надо получить данные из строки ячеек данных, где разные колонки хранят разные типы данных (целые числа, дробны числа, строки). Создадим вектор, который будет содержать элементы перечисления:

fn main() {
    #[derive(Debug)]
    enum SpreadsheetCell {
        Int(i32),
        Float(f64),
        Text(String),
    }

    let row = vec![
        SpreadsheetCell::Int(3),
        SpreadsheetCell::Text(String::from("blue")),
        SpreadsheetCell::Float(10.12),
    ];
    println!("{:?}", row);
}

Пример 8-1: Определение перечисления, которое будет иметь возможность содержать различные типы данных в векторе

Компилятору, обязательно, надо знать, какой тип данных будет у вектора во время компиляции, для того, чтобы рассчитать сколько памяти нужно будет выделить в куче для хранения одного элемента. Также используя перечисление и match можно будут обработаны все возможные варианты.