Tuple
A tuple groups a few values without declaring a type for them first. Its type is the list of its members' types in parentheses — (int32, bool) — and a value is written the same way, (17, true). Arrays hold many values of one type; a tuple holds a fixed handful of values of any types, side by side.
Writing and reading a tuple
The members are reached by position: .0 is the first, .1 the second, and so on:
let pair: (int32, float64) = (3, 2.5);
PrintLine("pair.0 is {} and pair.1 is {}", pair.0, pair.1);
| Collection | Element types | How many | Reached by |
|---|---|---|---|
| Array | all the same | fixed, any number | values[i] |
| Tuple | each its own | fixed, usually few | pair.0, .1 |
Like an array, a tuple is a plain value: assigning it copies every member, and let freezes the members too.
Several results from one function
The everyday use is returning more than one result. A function has a single return type, and a tuple lets that one type carry several values. Divide returns the quotient and whether it means anything — there is no number to return for a division by zero, so the second member says whether the first is real:
func Divide(numerator: int32, denominator: int32) -> (int32, bool) {
if denominator == 0 {
return (0, false);
}
return (numerator / denominator, true);
}
flowchart LR
a["`Divide(17, 5)`"] --> ra["`(3, true)`<br/>`.0` is the answer"]
b["`Divide(1, 0)`"] --> rb["`(0, false)`<br/>`.0` means nothing"]The caller checks the second member before trusting the first:
let good = Divide(17, 5);
if good.1 {
PrintLine("17 / 5 is {}", good.0);
}
The members need not share a type. Smallest returns the smallest value, an int32, and its position, a uint:
func Smallest(values: int32[..]) -> (int32, uint) {
Comparing tuples
Tuples of the same type compare member by member: equal when every member is equal. A tuple literal takes its member types from the other side, just as a lone 3 would:
PrintLine("good == (3, true) {}", good == (3, true));
A tuple inside a tuple
A member may itself be a tuple. The indexes chain, so outer.0.1 is the second member of the first member:
let outer: ((int32, int32), char8[..]) = ((3, 4), "point");
PrintLine("{} at {}, {}", outer.1, outer.0.0, outer.0.1);
Once a tuple's members need names to be understood — or it has more than two or three of them — it is time for a struct, or at least for the next lesson, Destructure.
The program
The whole lesson is one package in the Examples repository. Its comments explain every step.
// A tuple groups a few values without declaring a type for them first. Its type is the list of
// its members' types in parentheses — `(int32, bool)` — and a value is written the same way,
// `(17, true)`. The members are reached by position: `.0` is the first, `.1` the second.
//
// The everyday use is returning more than one result from a function. A function has a single
// return type, and a tuple lets that one type carry several values.
import Io::PrintLine;
// Two results from one call: the quotient, and whether it means anything. There is no number to
// return for a division by zero, so the second member says whether the first is real.
func Divide(numerator: int32, denominator: int32) -> (int32, bool) {
if denominator == 0 {
return (0, false);
}
return (numerator / denominator, true);
}
// The members need not share a type.
func Smallest(values: int32[..]) -> (int32, uint) {
var smallest = values[0];
var position: uint = 0;
for i in 1..values.length {
if values[i] < smallest {
smallest = values[i];
position = i;
}
}
return (smallest, position);
}
func Main() -> int {
// Written out in full: the type, then the value.
let pair: (int32, float64) = (3, 2.5);
PrintLine("pair.0 is {} and pair.1 is {}", pair.0, pair.1);
// Check the second member before trusting the first.
let good = Divide(17, 5);
if good.1 {
PrintLine("17 / 5 is {}", good.0);
}
let bad = Divide(1, 0);
if !bad.1 {
PrintLine("1 / 0 has no answer");
}
let temperatures: int32[5] = [12, 9, 4, 7, 11];
let coldest = Smallest(temperatures);
PrintLine("coldest {} at position {}", coldest.0, coldest.1);
// Tuples of the same type compare member by member. The literal `(3, true)` takes its member
// types from the other side, just as a lone `3` would.
PrintLine("good == (3, true) {}", good == (3, true));
PrintLine("bad == (3, true) {}", bad == (3, true));
// A tuple may hold another tuple. The indexes chain, so `outer.0.1` is the second member of
// the first member.
let outer: ((int32, int32), char8[..]) = ((3, 4), "point");
PrintLine("{} at {}, {}", outer.1, outer.0.0, outer.0.1);
return 0;
}
Run it
cd Examples/Sequences/Tuple
rux run
pair.0 is 3 and pair.1 is 2.5
17 / 5 is 3
1 / 0 has no answer
coldest 4 at position 2
good == (3, true) true
bad == (3, true) false
point at 3, 4
Common mistakes
Try it yourself
- Write
MinMax(values: int32[..]) -> (int32, int32)and print both members fortemperatures. - Declare
var point = (1, 2);, changepoint.0, and print both members. - Copy a
vartuple into a second variable, change the original, and check that the copy kept its old values. - Compare
good == (1, 2)and read the error.
Learn more
- Tuples and Tuples vs. structs in the Rux Reference
- Destructure — naming a tuple's members in one step
- Tuple pattern — matching on tuples in
match