Tuples

A tuple groups a fixed number of values, possibly of different types, into one value without declaring a type for it. The elements have positions but no names. The type is written like the value: a parenthesized, comma-separated list.

tuple-type  = "(" ")" | "(" type "," ")" | "(" type "," type { "," type } [ "," ] ")"
tuple-value = "(" ")" | "(" expression "," ")" | "(" expression "," expression { "," expression } [ "," ] ")"
let pair: (int, float64) = (1, 2.5);
let triple = ("Rux", 3, true);   // (char8[..], int, bool)
let one = (5,);                  // a tuple of one element

A one-element tuple needs a trailing comma; (5) is just 5 in parentheses.

Elements

Elements are read by position, counting from zero: .0, .1, and so on. On a var tuple they can be assigned the same way. Tuples nest, and so do the accesses:

var point = (0, 0);
point.0 = 4;
point = (point.0, 9);            // (4, 9)

let nested: (int, (char8[..], bool)) = (1, ("deep", true));
let word = nested.1.0;           // "deep"

An index beyond the last element is a compile-time error: tuple index 2 is out of range for a tuple with 2 elements.

Type identity

A tuple type is its element types, in order. (int, float64) and (float64, int) are different types, and two tuples with the same element types are the same type wherever they were written — a tuple has no name to tell it apart:

error: cannot assign '(int, float64)' to '(float64, int)'

A tuple is a value. Assigning it, passing it and returning it copies every element, and the copy is independent of the original. A tuple is how a function returns several values:

func Divide(dividend: int, divisor: int) -> (int, int) {
    return (dividend / divisor, dividend % divisor);
}

The usual way to use such a result is to take it apart at once, let (quotient, remainder) = Divide(17, 5);; see Destructuring.

The unit type

() is the tuple with no elements, the unit type, and its only value is also written (). It is the success type of a fallible function that produces nothing on success: ! E is exactly () ! E. A function may return () explicitly, func Reset() -> (), which is not the same as leaving out the result type; see Function declarations.

sizeof(()) is 0.

Equality

Two tuples of the same type compare with == and !=, element by element from left to right, stopping at the first difference. Every element type must support ==:

let same = (1, 'a') == (1, 'a');   // true
let differ = (1, 2) != (1, 3);     // true
error: structural equality for '(int, char8[..])' is unavailable because element type 'char8[..]' has no '==' operator

Tuples have no built-in ordering. (1, 2) < (1, 3) is operator '<' is not defined for tuple '(int, int)', with the note tuples have structural equality but no built-in ordering.

Matching

A tuple pattern takes a tuple apart in a match arm and tests its elements at the same time. A match on a tuple is exhaustive when the combinations of its arms cover every value, so the four (bool, bool) combinations need no else:

func Describe(flags: (bool, bool)) -> char8[..] {
    return match flags {
        (true, true) => "both",
        (true, false) => "first",
        (false, true) => "second",
        (false, false) => "neither"
    };
}

See Patterns.

Tuples or structs

Both group several values. A tuple's elements are anonymous, so nothing at the use says what .1 means; a struct's are named, and the struct is a type of its own that cannot be mixed up with another of the same shape.

A tuple fits when…A struct fits when…
two or three values, their meaning obvious from contextmore values, or meanings that need labels
the grouping is local, such as a function's resultsthe type is used across the program
no behaviour is attachedthe type needs methods, constructors or operators
any (int, int) will doa Point must not be confused with a Size

Tuples cannot be extended with methods; see Extensions.

Layout

A tuple's elements are stored in order, each at the next offset its alignment allows, like the fields of a struct: sizeof((uint8, uint32)) is 8. See Memory layout.

See also