Vectors

Vectors

Вектор - множество данных одного типа, количество которых можно изменять: добавлять и удалять элементы. Нужен, когда:

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

Методы

// Задание пустого вектора:
// let mut a test_vector: Vec<i32> = Vec::new();  

// Задание вектора со значениями через макрос:
let mut test_vector = vec![1, 2, 3, 4];  

test_vector.push(42);  // добавить число 42 в конец mut вектора
let Some(last) = test_vector.pop(); // удаляет и возвращает последний элемент (возвращает Option<T>)
test_vector.remove(0);  // удалить первый элемент =1
  
for i in &mut test_vector {  // пройти вектор как итератор для вывода
*i += 1; // изменять значения при их получении требует делать '*' dereference
println!("{i}"); }

println!("Vector length: {}", test_vector.len()); // количество элементов

Получение элемента вектора

Элемент можно получить либо с помощью индекса, либо с помощью безопасного метода get:

let mut test_vector = vec![1,2,3,4,5];  
println!("Third element of vector is: {}", &test_vector[2]);  // индекс
  
let third: Option<&i32> = test_vector.get(2);  // безопасный метод get
match third {  
    Some(third) => println!("Third element of vector is: {}", third),  
    None => println!("There is no third element")  
}

Разница в способах в реакции на попытку взять несуществующий элемент за пределами вектора. Взятие через индекс приведёт к панике и остановке программы. Взятие с помощью get сопровождается проверкой и обработкой ошибки.

Удаление элемента

Метод .remove(index):

let mut numbers = vec![1, 2, 3, 4];
numbers.remove(1); // удаляет элемент с индексом 1
println!("{:?}", numbers); // [1, 3, 4]
  • .remove() сдвигает все последующие элементы, что может быть дорого для больших векторов (O(n));
  • Возвращает удалённый элемент;
  • Требует mut, так как изменяет вектор;
  • Индекс должен быть в пределах длины, иначе паника.

Хранение элементов разных типов в векторе

Rust нужно заранее знать при компиляции, сколько нужно выделять памяти под каждый элемент. Если известны заранее все типы для хранения, то можно использовать промежуточный enum:

#[derive(Debug)]  
enum SpreadSheet {  
    Int(i32),  
    Float(f64),  
    Text(String)  
}  
  
fn main() {  
    let row = vec![  
      SpreadSheet::Int(42),  
      SpreadSheet::Float(3.14),  
      SpreadSheet::Text(String::from("red"))  
    ];  
  
    for i in row {  
        println!("{:?}",i);  
    }  }

Vector со строками String

let mut v: Vec<String> = Vec::new();

Пустой вектор с нулевыми строками можно создать через Default размером до 32 элементов (Rust 1.47):

let v: [String; 32] = Default::default();

Вектор большего размера можно создать через контейнер Vec:

let mut v: Vec<String> = vec![String::new(); 100];

Вектор с заданными строками можно инициализировать либо с помощью метода to_string(), либо через определение макроса:

macro_rules! vec_of_strings {
    ($($x:expr),*) => (vec![$($x.to_string()),*]);
}

fn main()
{
    let a = vec_of_strings!["a", "b", "c"];
    let b = vec!["a".to_string(), "b".to_string(), "c".to_string()];
    assert!(a==b); // True
}

Соединение вектора со строками в строку (Join):

result_vec.join(" "); // указывается разделитель для соединения
// в старых версиях Rust <1.3 применяют метод .connect();

Сортировка

let number_vector = vec!(1,12,3,1,5);   
number_vector.sort(); // 1,1,3,5,12

Способы реверс-сортировки

Смена элементов при сравнении, метод .sort_by() принимает замыкание (closure) для пользовательской сортировки:

number_vector.sort_by(|a,b| b.cmp(a));
  • |a, b| — это замыкание;
  • b.cmp(a) возвращает порядок: Ordering::LessEqual или Greater. Инверсия (b.cmp(a) вместо a.cmp(b)) даёт убывающий порядок. Альтернатива: .sort_by_key() для сортировки по вычисляемому ключу:
let mut numbers = vec![3, 1, 4, 1, 5];
numbers.sort_by_key(|&x| -x); // по убыванию через отрицание
println!("{:?}", numbers); // [5, 4, 3, 1, 1]

Сортировка, потом реверс:

number_vector.sort();
number_vector.reverse();

Обёртка Reverse с экземпляром Ord:

use std::cmp::Reverse;
number_vector.sort_by_key(|w| Reverse(*w));

Если вернуть Reverse со ссылкой и без *, это приведёт к проблеме с временем жизни.

Сортировка вектора по ключу

use std::collections::HashSet;

fn main() {
    let mut vowels = HashSet::new();
    vowels.insert('e');
    vowels.insert('a');
    vowels.insert('i');
    vowels.insert('o');
    vowels.insert('u');
    
    // Конвертация в Vec и сортировка
    let mut vowel_vec: Vec<char> = vowels.into_iter().collect();
    // Свой порядок сортировки: a, e, i, o, u
    let vowel_order = |c: &char| match c {
        'a' => 0,
        'e' => 1,
        'i' => 2,
        'o' => 3,
        'u' => 4,
        _ => 5,
    };
    
    vowel_vec.sort_by_key(vowel_order);
    println!("Sorted vowels: {:?}", vowel_vec); // ['a', 'e', 'i', 'o', 'u']
}

Дедупликация вектора

Удаление одинаковых элементов в векторе, похоже на работу с HashSet.

let s = "aabbccdddeeeeffffeee";
let mut chars: Vec<char> = s.chars().collect();
// Сначала отсортировать, чтобы собрать одинаковые элементы вместе
chars.sort_unstable(); 
// dedup() удаляет на месте одинаковые СТОЯЩИЕ РЯДОМ в векторе элементы
chars.dedup();
// собрать назад в String:
let unique_s: String = chars.into_iter().collect();

Получение вектора из итератора

    let collected_iterator: Vec<i32> = (0..10).collect();
    println!("Collected (0..10) into: {:?}", collected_iterator);
    // Collected (0..10) into: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Конвертация

Конвертация из массива array в vector:

let number_list = [1,12,3,1,5,2];  
let number_vector = number_list.to_vec(); // перевод array[i32] -> vector<i32>

Вариант через итератор:

let a = [10, 20, 30, 40]; 
let v: Vec<i32> = a.iter().map(|&e| e as i32).collect(); 

Вектор из байтов vector of bytes в строку String:

use std::str;

fn main() {
    let buf = &[0x41u8, 0x41u8, 0x42u8]; // vector of bytes
    let s = match str::from_utf8(buf) {
        Ok(v) => v,
        Err(e) => panic!("Invalid UTF-8 sequence: {}", e),
    };
    println!("result: {}", s);
}