Variables, Arrays, Constants
Note
When in doubt on variable type: just use i32 for everything! i32 is the default in Rust, and the fastest, even on x64 architecture.
Scalar Types
| Unsigned | Signed |
|---|---|
| u8 | i8 |
| u16 | i16 |
| u32 | i32 |
| u64 | i64 |
| u128 | i128 |
| usize | isize |
| Floating point vars: f32, f64. |
You can declare number variables with _ sign in any place for convenience:
let x: i32 = 1_000_000;
let (mut missiles: i32, ready: u32) = (8, 5); // 2 vars tuple assign in 1 line
You can also add the var type to the number (convenient with _ when using generics):
let x = 1000_u32;
let y = 3.14_f32;Converting
String2Int:
let n_str = "123456789".to_string();
let n_int = n_str.parse::<i32>().unwrap();Char2Int:
let letter = 'a';
println!("{}", letter as u32 - 96); // = 97-96 = 1
let i = 97u8; // только с u8 разрешено делать 'as char'
println!("Value: {}", i as char);Boolean type
bool type can be true or false. Non-integer - do NOT try to use arithmetic on these. But you can cast them:
true as u8;
false as u8;Mutability
By default, variables in Rust are immutable. To make a variable mutable, special mut identifier must be placed. Rust compiler may infer the variable type from the type of value.
let x = 5; // immutable variable, type i32 guessed by Rust as default for numbers.
let mut x = 5; // mutable variable
Shadowing
Variable names can be reused. This is not mutability, because shadowing always re-creates the variable from scratch. Previous variable value may be used:
let x = 5;
let x = x + 1; // new variable created with value = 6
Constants
Constant values are always immutable and available in the scope they were created in throughout the whole program. Type of constant must always be defined. Constants may contain results of operations. They are evaluated by Rust compiler. List of evaluations: https://doc.rust-lang.org/stable/reference/const_eval.html
const ONE_DAY_IN_SECONDS: u32 = 24 * 60 * 60; // type u32 MUST be defined
let phrase = "Hello World";
println!("Before: {phrase}"); // Before: Hello World
let phrase = phrase.len();
println!("After: {phrase}"); // After: 11
Составные переменные
Кортеж / Tuple
Кортеж — это структура с фиксированным размером, которая может содержать элементы разных типов. Синтаксис: (T1, T2, ...).
let tup: (u32, f32, i32) = (10, 1.2, -32, "hello");
// Деструктуризация позволяет "разобрать" кортеж на отдельные переменные:
let (x,y,z,u) = tup;
//Доступ осуществляется через точку и индекс (начиная с 0):
let a1 = tup.0;
let a2 = tup.1;Если нужен только один элемент, остальные можно игнорировать с помощью _:
let (left, right) = slice.split_at(middle);
let (_, right) = slice.split_at(middle);Особенности:
- Кортежи полезны для возврата нескольких значений из функции;
- Как и массивы, они хранятся в стеке и имеют фиксированный размер;
- Используйте кортежи, когда нужно объединить разнородные данные в одну сущность.
Массив / Array
Массив в Rust — это структура данных с фиксированным размером, которая хранит элементы одного типа. Его синтаксис: [T; N], где:
T— тип элементов (например,i32для целых чисел),N— количество элементов, известное на этапе компиляции.
fn main() {
// Простой массив из трёх чисел
let numbers: [i32; 3] = [1, 2, 3];
// Массив с повторяющимся значением (5 элементов, все равны 0)
let zeros: [i32; 5] = [0; 5];
println!("Numbers: {:?}", numbers); // Вывод: [1, 2, 3]
println!("Zeros: {:?}", zeros); // Вывод: [0, 0, 0, 0, 0]
// Доступ к элементам массива = по индексу (начиная с 0)
let first = numbers[0];
let second = numbers[1]; // accessing array elements
}❗Попытка обратиться к несуществующему индексу (например, arr[3] для массива из трёх элементов) вызовет панику!
Особенности:
- Размер массива фиксирован и не может измениться после создания;
- Массивы хранятся в стеке (stack), что делает их быстрыми, но менее гибкими;
- Используйте массивы, когда размер известен заранее и не будет меняться.
chunks(usize) - разбиение массива на куски с остатком
Можно разбивать срез на части заданного размера. Последний кусок может быть меньше указанного размера, если элементов не хватает.
let numbers = [1, 2, 3, 4, 5, 6, 7];
let chunks = numbers.chunks(3); // разбить на куски по 3
for chunk in chunks {
println!("Чанк: {:?}", chunk);
}
// Вывод:
// Чанк: [1, 2, 3]
// Чанк: [4, 5, 6]
// Чанк: [7] <-- меньше размера 3!
chunks_exact(usize) - точное разбиение
Разбивает срез на части строго заданного размера. Остаток (если есть) доступен отдельно через метод remainder().
let numbers = [1, 2, 3, 4, 5, 6, 7];
let mut chunks = numbers.chunks_exact(3);
println!("Полные чанки:");
for chunk in chunks.by_ref() {
println!(" {:?}", chunk); }
println!("Остаток: {:?}", chunks.remainder());
// Вывод:
// Полные чанки:
// [1, 2, 3]
// [4, 5, 6]
// Остаток: [7]
windows(usize) - разбиение с перекрытием
Работает похоже на chunks, но с перекрытием:
let nums = [1, 2, 3, 4];
for window in nums.windows(2) {
println!("{:?}", window); } // [1,2], [2,3], [3,4]
Замечания по chunks(), chunks_exact(), windows()
- Только для срезов (slices): методы работают только с типами, реализующими трейт
SlicePattern, в основном&[T]; - Заимствование: Чанки возвращают ссылку (
&[T]) на исходные данные, а не копию; - Размер чанка: Размер должен быть положительным числом больше 0 (иначе паника);
- Производительность:
chunks_exact()быстрее, так как не проверяет размер последнего чанка.
Подсчёт одинаковых элементов в массиве с помощью itertools…
Удаление переменных
Естественное удаление при выходе из области видимости
Переменная выходит из области видимости (закрывающая фигурная скобка }), её память освобождается автоматически, если она владеет данными (например,String, Vec).
fn main() {
let text = String::from("Hello");
println!("{}", text); // "Hello"
// Здесь text всё ещё существует
} // text выходит из области видимости и память освобождается
// println!("{}", text); // Ошибка: text больше не существует
Очистка содержимого c clear()
fn main() {
let mut text = String::from("Hello");
text.clear(); // очищает содержимое, но переменная остаётся
println!("{}", text); // "" (пустая строка)
}Использование std::mem::drop
Для явного “удаления” переменной (освобождения её памяти) до конца области видимости можно использовать функцию std::mem::drop:
fn main() {
let mut text = String::from("Hello");
println!("{}", text); // "Hello"
std::mem::drop(text); // text "удаляется" (перестаёт существовать)
// println!("{}", text); // Ошибка: text больше не существует
}drop принимает владение переменной и немедленно освобождает её память. После этого переменная становится недоступной.