lang::tuple¶
Fixed-length group of values whose element types may differ.
A tuple is written with parentheses and needs no import. Its type is the
parenthesised list of its element types, so (1, "two", 3.0) has type
(i64, String, f64).
Unlike a Vec<T>, a tuple's length is part of its type and its elements do not
have to share a type. Unlike a struct, its fields are positional and it needs no
declaration.
Construction¶
let pair = (3, 4) // (i64, i64)
let mixed = (1, "two", 3.0) // (i64, String, f64)
let single = (5,) // one-element tuple; the comma is required
let unit = () // the empty tuple
(5) is a parenthesised expression, not a tuple - the trailing comma in (5,)
is what makes it one.
Positional access¶
Elements are read with .0, .1, .2, and so on. Reads chain through nested
tuples:
A mut binding assigns positionally, including through a nested tuple:
Destructuring¶
A tuple pattern binds every element at once, in let, in for, in match, and
in a function's parameter list:
let (id, name, weight) = (1, "two", 3.0)
for (key, value) in map.iter() {
println!("{key}={value}")
}
fn label((rank, name): (i64, String)) -> String {
format!("{rank}: {name}")
}
match point {
(0, 0) => println!("origin"),
(x, _) => println!("x = {x}"),
}
Comparison and ordering¶
Tuples compare structurally, element by element in declaration order, with no
#[derive(...)]. Equality needs every element equal; ordering is
lexicographic, so the first differing element decides.
That ordering is what sort uses on a sequence of tuples, which makes a tuple
the usual sort key:
Where tuples appear¶
A tuple is an ordinary value: it can be a function return, a struct field, a
Vec element, a Map key, or a channel payload.
fn min_max(xs: &[i64]) -> (i64, i64) {
(xs.min().unwrap_or(0), xs.max().unwrap_or(0))
}
struct Reading { at: (i64, i64), value: f64 }
let by_position: Map<(i64, i64), String> = Map::new()
Map::iter() yields [(K, V)], and Vec::enumerate() yields
Vec<(i64, T)>, so the for (a, b) in ... shape reads the same everywhere.
Methods¶
A tuple's surface is mostly syntax: positional access, destructuring, and structural comparison. Its methods are the four that do not assume a sequence:
| Method | Returns |
|---|---|
len() |
element count, folded at compile time from the type |
is_empty() |
true only for () |
get(i) |
element at a runtime index; prefer t.0 when the position is known |
clone() |
a copy of the tuple |
to_string() |
(a, b, ...), the text {} and {:?} produce |
into() / try_into() |
conversion through a From / TryFrom impl |
iter() and the combinators built on it are rejected: a tuple's elements may
differ in type, so there is no element type to yield. Walk a tuple by
destructuring it, not by iterating it.
Discovery¶
%info Tuple in the REPL describes the type, and %explain <binding> on a
tuple binding lists its positional elements and their types: