Составные типы данных

Кортежи

Последнее обновление: 08.05.2024

Кортеж (tuple) представляет способ группировки данных разных типов в один составной тип. Кортежи имеют фиксированный тип в соответствии с вложенными данными. После определения кортежа его размер нельзя ни уменьшить, ни увеличить.

Формальный синтаксис определения кортежа:

let tuple = (значение1, значение2, ... значениеN);

Кортеж определяется в круглых скобках, внутри которых через запятую перечисляются составляющие кортеж значения.

Определение простейшего кортежа:

let user = ("Tom", 36, 1.78);

Здесь переменная user представляет кортеж, который состоит из трех элементов.

В примере выше тип элементов кортежа выводится автоматически, но также мы можем явно задать их типы:

let user: (&str, u8, f32) = ("Tom", 36, 1.78);

То есть в данном случае кортеж имеет тип (&str, u8, f32), а это значит, что первый его элемент должен представлять строку, второй элемент - число типа u8, а третий - число с плавающей точкой типа f32.

После объявления кортежа мы можем обращаться к его элементам в виде:

название_кортежа.индек_элемента

После названия кортежа через точку указывается индекс элемента кортежа, при этом индексация начинается с нуля. Например:

fn main(){

	let user: (&str, u8, f32) = ("Tom", 36, 1.78);
	println!("Имя: {}", user.0);	
	println!("Возраст: {}", user.1);
	println!("Рост: {}", user.2);
}

Консольный вывод:

Имя: Tom
Возраст: 36
Рост: 1.78

Также мы можем изменять элементы кортежа, если его переменная определена с ключевым словом mut:

fn main(){

	let mut user: (&str, u8, f32) = ("Tom", 36, 1.78);
	user.0 = "Bob";
	println!("Имя: {}", user.0);	// Bob
}

Декомпозиция

Rust предоставляет такую возможность, как декомпозиция (destructuring) кортежа. Например, мы могли бы следующим образом присвоить значения кортежа переменным:

fn main(){

	let user = ("Tom", 36, 1.78);
	// присваиваем переменным значения кортежа
	let name = user.0;
	let age = user.1;
	let height = user.2;

	println!("Имя: {}", name);	
	println!("Возраст: {}", age);
	println!("Рост: {}", height);
}

Но декомпозиция позволяет сократить код:

fn main(){

	let user = ("Tom", 36, 1.78);
	// декомпозиция кортежа на переменные
	let (name, age, height) = user;
	
	println!("Имя: {}", name);	
	println!("Возраст: {}", age);
	println!("Рост: {}", height);
}

Но, возможно, нам нужны не все данные из кортежа. Например, нам не нужно получать рост человека. Тем не менее мы не можем написать, например, так:

let (name, age) = user;	// !Ошибка

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

let (name, age, _) = user;

Сравнение кортежей

Для сравнения кортежей применяется операция ==. Кортежи считаются равными, если равны их соответствующие значения. Пример сравнения кортежа:

fn main() {
 
    let user = ("Tom", 36);
	
	if ("Tom", 36) == user {
        println!("name: Tom, age: 36");
    }
}

Владение кортежем

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

fn main(){

	let user  = (String::from("Tom"), 39);
    let employee = user;    // передача владения переменной employee

	println!("User Name: {}", user.0);	// ! Ошибка
	println!("Employee Name: {}", employee.0);
}

Применяя ссылки, мы можем уйти от данной ошибки:

let user  = (String::from("Tom"), 39);
let employee = &user;    // нет передачи владения

Кортеж как параметр и результат функции

Как и любой другой тип, кортеж может выступать параметром функции. Например:

fn display(user:(&str, i32)){
	
	println!("name: {}  age:{}", user.0, user.1);
}
fn main(){
     
    let tom = ("Tom", 36);
	display(tom);
}

Здесь функции display() принимает кортеж, причем первый элемент этого кортежа представляет тип &str или строку, а второй элемент - тип i32.

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

// декомпозиция кортежа
fn display((name, age):(&str, i32)){
	
	println!("name: {}  age:{}", name, age);
}

fn main(){
     
    let tom = ("Tom", 36);
	display(tom);
}

Но стоит учитывать, что если кортеж содержит данные типов, которые не реализуют трейт Copy, то при передаче кортежа происходит передача владения. Например:

fn display(user:(String, i32)){
	
	println!("name: {}  age:{}", user.0, user.1);
}
fn main(){
     
    let tom = (String::from("Tom"), 36);
	display(tom);
	display(tom); // ! Ошибка
}

Здесь при первом вызове функции display происходит передача владения кортежа. В итоге мы получаем ошибку. И в этом случае мы можем передавать кортеж по ссылке:

// получаем ссылку на кортеж
fn display(user: &(String, i32)){
	
	println!("name: {}  age:{}", user.0, user.1);
}
fn main(){
     
    let tom = (String::from("Tom"), 36);
	display(&tom);
	display(&tom);
}

И также функция может возвращать кортеж:

fn get_default_point() -> (i32, i32){
	(4, 25)
}
fn main(){
     
    let (x, y) = get_default_point();
	println!("x: {}  y:{}", x, y);
}

Здесь функция get_default_point() возврашает кортеж, первый и второй элементы которого представляют тип i32.

Помощь сайту
Юмани:
410011174743222
Номер карты:
4048415020898850