Примитивный тип tuple

Кортеж — конечная разнородная последовательность: (T, U, ..).

Рассмотрим каждое свойство по порядку:

Свойства кортежей

  1. Конечность: Кортеж имеет длину. Например, кортеж длины 3:

    #![allow(unused)]
    fn main() {
    ("hello", 5, 'c');
    }

    Длина также называется "арностью" (arity). Каждый кортеж разной длины — это отдельный, уникальный тип.

  2. Разнородность: Каждый элемент кортежа может иметь разный тип. Указанный выше кортеж имеет тип:

    #![allow(unused)]
    fn main() {
    (&'static str, i32, char)
    }
  3. Последовательность: Элементы кортежа доступны по позиции с помощью "индексации кортежа":

    #![allow(unused)]
    fn main() {
    let tuple = ("hello", 5, 'c');
    
    assert_eq!(tuple.0, "hello");
    assert_eq!(tuple.1, 5);
    assert_eq!(tuple.2, 'c');
    }

Последовательная природа кортежа влияет на реализации различных трейтов. Например, в PartialOrd и Ord элементы сравниваются последовательно до первого несовпадающего набора.

Реализации трейтов

В документации используется сокращение (T₁, T₂, …, Tₙ) для представления кортежей различной длины. Когда это используется, любая граница трейта, выраженная на T, применяется к каждому элементу кортежа независимо. Это обозначение для удобства документации, а не валидный синтаксис Rust.

Трейты с ограничением по длине (до 12 элементов)

Из-за временных ограничений в системе типов Rust следующие трейты реализованы только для кортежей длиной до 12 элементов:

ТрейтОписание
PartialEq, EqПроверка на равенство
PartialOrd, OrdСравнение
DebugОтладочный вывод
DefaultЗначение по умолчанию
HashХеширование
From<[T; N]>Преобразование из массива

Трейты для кортежей любой длины

Следующие трейты реализованы для кортежей любой длины. Их реализации автоматически генерируются компилятором:

ТрейтОписание
Clone, CopyКлонирование и копирование
Send, SyncМногопоточность
UnpinПеремещение
UnwindSafe, RefUnwindSafeБезопасность при раскрутке стека

Таблица реализаций трейтов

Основные трейты

ТрейтЭлементыВерсияПримечание
DebugT: Debug1.0.0До 12 элементов
DefaultT: Default1.0.0До 12 элементов
HashT: Hash1.0.0До 12 элементов
OrdT: Ord1.0.0До 12 элементов
PartialEqT: PartialEq1.0.0До 12 элементов
PartialOrdT: PartialOrd1.0.0До 12 элементов
EqT: Eq1.0.0До 12 элементов

Трейты коллекций

ТрейтТипВерсияОписание
Extend<(K, V)>BTreeMap<K, V, A>1.0.0Расширение BTreeMap парами
Extend<(K, V)>HashMap<K, V, S>1.0.0Расширение HashMap парами
Extend<(&K, &V)>BTreeMap<K, V, A>1.2.0Расширение ссылками на пары
Extend<(&K, &V)>HashMap<K, V, S>1.4.0Расширение ссылками на пары
Extend<(T₁, T₂, …, Tₙ)>(ExtendT₁, ExtendT₂, …)1.56.0Расширение кортежей коллекций
FromIterator<(K, V)>BTreeMap<K, V>1.0.0Создание из итератора пар
FromIterator<(K, V)>HashMap<K, V, S>1.0.0Создание из итератора пар
FromIterator<(T₁, T₂, …, Tₙ)>(ExtendT₁, ExtendT₂, …)1.79.0Создание кортежей коллекций

Преобразования From/Into

ПреобразованиеНазначениеВерсияОписание
[T; N](T₁, T₂, …, Tₙ)Кортеж1.71.0До 12 элементов
(T₁, T₂, …, Tₙ)[T; N]Массив1.71.0До 12 элементов
(I, u16)SocketAddrАдрес сокета1.17.0Из IP-адреса и порта

Трейты для работы с диапазонами

ТрейтТипВерсияОписание
RangeBounds<T>(Bound<T>, Bound<T>)1.28.0Границы диапазона
RangeBounds<T>(Bound<&T>, Bound<&T>)1.28.0Границы диапазона по ссылке
IntoBounds<T>(Bound<T>, Bound<T>)nightlyПреобразование в границы
SliceIndex<[T]>(Bound<usize>, Bound<usize>)1.53.0Индексация срезов
SliceIndex<str>(Bound<usize>, Bound<usize>)1.73.0Индексация строк

Сетевые трейты

ТрейтТипВерсияОписание
ToSocketAddrs(&str, u16)1.0.0Преобразование в адреса сокетов
ToSocketAddrs(IpAddr, u16)1.0.0Преобразование в адреса сокетов
ToSocketAddrs(Ipv4Addr, u16)1.0.0Преобразование в адреса сокетов
ToSocketAddrs(Ipv6Addr, u16)1.0.0Преобразование в адреса сокетов
ToSocketAddrs(String, u16)1.46.0Преобразование в адреса сокетов

Примеры

Базовое использование

#![allow(unused)]
fn main() {
let tuple = ("hello", 5, 'c');
assert_eq!(tuple.0, "hello");
}

Использование как возвращаемого типа

#![allow(unused)]
fn main() {
fn calculate_point() -> (i32, i32) {
    (4, 5)
}

let point = calculate_point();
assert_eq!(point.0, 4);
assert_eq!(point.1, 5);

// С использованием паттернов
let (x, y) = calculate_point();
assert_eq!(x, 4);
assert_eq!(y, 5);
}

Создание из массива

#![allow(unused)]
fn main() {
let array: [u32; 3] = [1, 2, 3];
let tuple: (u32, u32, u32) = array.into();
}

Использование Extend с кортежами коллекций

#![allow(unused)]
fn main() {
let mut tuple = (vec![0], vec![1]);
tuple.extend([(2, 3), (4, 5), (6, 7)]);
assert_eq!(tuple.0, [0, 2, 4, 6]);
assert_eq!(tuple.1, [1, 3, 5, 7]);
}

Использование FromIterator

#![allow(unused)]
fn main() {
let string = "1,2,123,4";
let (numbers, lengths): (Vec<_>, Vec<_>) = string
    .split(',')
    .map(|s| s.parse().map(|n: u32| (n, s.len())))
    .collect::<Result<_, _>>()?;

assert_eq!(numbers, [1, 2, 123, 4]);
assert_eq!(lengths, [1, 1, 3, 1]);
}

Использование как диапазона

#![allow(unused)]
fn main() {
let range = (Bound::Included(0), Bound::Excluded(10));
let slice = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let subslice = &slice[range];
assert_eq!(subslice, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
}

Особые реализации

Служебные трейты (для любых длин)

  • CloneFromCell — для кортежей до 12 элементов (если T: CloneFromCell)
  • ConstParamTy_ — для кортежей до 12 элементов (если T: ConstParamTy_)
  • StructuralPartialEq — для кортежей до 12 элементов

Автоматические реализации (для любых длин)

  • Freeze, RefUnwindSafe, Send, Sync, Unpin, UnwindSafe — если все элементы реализуют соответствующий трейт

Примечания

  1. Индексация: Индексы кортежа начинаются с 0: tuple.0, tuple.1, и т.д.
  2. Сравнение: Кортежи сравниваются лексикографически, последовательно сравнивая элементы.
  3. Длина: В текущей версии Rust (1.0.0+) многие трейты ограничены кортежами длиной до 12 элементов, но это может измениться в будущем.
  4. Паттерны: Кортежи отлично работают с деструктуризацией в паттернах.