Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Returning and unpacking tuples

A tuple groups values with different types. Write (value,) for one component; parentheses without a comma simply group an expression. Function signatures may spell a tuple as (i64, str) or tuple[i64, str]. Unpacking transfers owned components, and _ discards a component. Indices must be integer literals; as with lists, a negative index counts from the end, so pair[-1] is the last component.

def measurement() -> (i64, str):
    (42, "cm")

def main() -> Result[(), IoError]:
    value, unit = measurement()
    print(value)?
    print(unit)?
    print((3, "three"))?
    Ok(())
42
cm
(3, "three")

Creating (a, b) does not allocate a tuple, so it needs no ?. Operations that produce its components, such as the list construction below, can still fail.

Nested tuples can be unpacked in one statement. Match each tuple’s shape, using _ for a component you do not need:

def main() -> Result[(), IoError | OverflowError[i64]]:
    ((x, y), label) = ((3, 4), "point")
    print(label, x + y)?
    Ok(())
point 7

Tuples of numbers, strings, or nested tuples of those can be compared. The first differing component decides the result, just as in sort(). Comparisons leave both values available:

def main() -> Result[(), IoError]:
    first = (1, "apple")
    second = (1, "pear")
    print(first < second, first <= first)?
    print(first, second)?
    Ok(())
True True
(1, "apple") (1, "pear")

Tuple float components follow sorting’s total order: -0.0 precedes 0.0, and NaNs come last. This differs from scalar float comparison; tuple equality still uses the components’ ordinary equality.

Distinct tuple components can be borrowed and read independently. The borrow below protects component 1 while component 0 remains readable:

def main() -> Result[(), AllocError | IoError]:
    mut pair = (1, [2]?)
    items = &mut pair[1]
    first = pair[0]
    print(first)?
    print(pair[0])?
    items.append(3)?
    print(pair)?
    Ok(())
1
1
(1, [2, 3])

Copyable components can be changed through mutable references too. A saved copy has its own inline storage:

def main() -> Result[(), IoError]:
    mut pair = (1, 2)
    saved = pair
    first = &mut pair[0]
    *first = 5
    print(pair)?
    print(saved)?
    Ok(())
(5, 2)
(1, 2)

Use a reference for the write; direct pair[0] = 5 is not supported. Reading an exclusively borrowed component is rejected:

def main() -> Result[(), AllocError | IoError]:
    mut pair = (1, [2]?)
    items = &mut pair[1]
    print(pair[1])?
    items.append(3)?
    Ok(())
conflicting borrow: cannot read or borrow `pair[1]` while it is exclusively borrowed