Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Деструктурирующее присваивание

В главе Кортежи мы познакомились со специальным синтаксисом для “разбиения” объекта кортежа на его составляющие.

#![allow(unused)]
fn main() {
let employee: (&str, i32, bool) = ("John Doe", 1980, true);
let (name, birth_year, is_active_employee) = employee;
}

Такая операция “разбиения” объекта на составляющие называется деструктурирующим присваиванием.

Деструктурирующее присваивание доступно не только для кортежей, но также для массивов и структур. Рассмотрим несколько примеров.

Кортежи

С деструктурированием кортежей мы уже знакомы, однако давайте взглянем на более сложный пример с вложенным кортежем.

#![allow(unused)]
fn main() {
let tup: (i32,char,bool,(i32,i32,i32)) = (1, 'z', true, (7,7,7));
let (num, c, _, t) = tup;
println!("num={}, char={}, triplet={:?}", num, c, t);
}

Здесь мы присвоили весь вложенный кортеж целиком в переменную t, однако мы можем деструктурировать и вложенный кортеж.

#![allow(unused)]
fn main() {
let tup: (i32,char,bool,(i32,i32,i32)) = (1, 'z', true, (7,8,9));
let (num, c, _, (d1, d2, d3)) = tup;
println!("num={num}, char={c}, d1={d1}, d2= {d2}, d3={d3}");
}

Обратите внимание, что для элемента, значение которого нам не интересно, мы использовали “выброшенную” (discarded) переменную.

Массивы

Массивы также можно деструктурировать.

#![allow(unused)]
fn main() {
let arr: [i32;3] = [1, 2, 3];
let [a1, a2, a3] = arr;
println!("a1={a1}, a2={a2}, a3={a3}");
}

При деструктурировании массива можно часть элементов, идущих с начала массива, присвоить переменным, а оставшиеся элементы (в хвосте массива) проигнорировать.

#![allow(unused)]
fn main() {
let arr: [i32;5] = [1, 2, 3, 4, 5];
let [a_1, _, a_3, ..] = arr;
println!("a1={}, a3={}", a_1, a_3);
}

Также оставшиеся элементы в хвосте массива можно не игнорировать, а записать в новый массив:

#![allow(unused)]
fn main() {
let arr: [i32;5] = [1, 2, 3, 4, 5];
let [a_1, _, a_3, rest @ ..] = arr;
// Тип переменной rest - массив [i32, 2]
println!("a1={}, a3={}, rest={:?}", a_1, a_3, rest);
}

Выражение rest@.. означает: привязать все остальные элементы к переменной rest.

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

Структуры

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

struct Person { name: String, age: u32 }

fn main() {
    let p = Person { name: String::from("John"), age: 25 };
    let Person { name, age } = p;
    println!("Name={}, Age={}", name, age);
}

При деструктурировании структуры мы также можем “извлечь” только те поля, которые нам нужны:

struct Person { name: String, age: u32 }

fn main() {
    let p = Person { name: String::from("John"), age: 25 };
    let Person { name, .. } = p;
    println!("Name={name}");
}

Деструктурирование аргументов

Производить деструктурирование объектов можно не только при присваивании, но и при принятии аргументов в функцию.

struct Point2D {
    x: i32,
    y: i32,
}

fn print_point_2d(Point2D { x, y }: Point2D) {
    println!("2D point ({x}, {y})");
}

fn main() {
    let p = Point2D { x: 1, y: 5 };
    print_point_2d(p);
}

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

// Red, Green, Blue, Alpha
struct Rgba(u8, u8, u8, u8);

// Проверяет, если цвет НЕ является полупрозрачным,
// т.е. alpha компонента равна 255
fn is_fully_opaque(Rgba(_, _, _, alpha): Rgba) -> bool {
    alpha == 255
}

fn main() {
    println!("Is opaque: {}", is_fully_opaque(Rgba(125, 0, 0, 255)));
    println!("Is opaque: {}", is_fully_opaque(Rgba(200, 200, 200, 50)));
}