Деструктурирующее присваивание
В главе Кортежи мы познакомились со специальным синтаксисом для “разбиения” объекта кортежа на его составляющие.
#![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)));
}