Tuples
When implementing the arithmetic opcodes, we run into a situation where a single operation produces two pieces of information that are equally important: the result of the calculation, and whether it overflowed. In most languages you would use an output parameter or a struct. In Rust, the idiomatic answer is a tuple.
What is a tuple?
A tuple groups a fixed number of values that can have different types:
#![allow(unused)] fn main() { let pair: (u8, bool) = (255, true); let triple: (u8, u16, bool) = (10, 300, false); }
Unlike arrays, each position can hold a different type. The type of the tuple is part of the type signature — (u8, bool) and (u16, bool) are different types.
You can access elements by index:
#![allow(unused)] fn main() { let pair = (0x42u8, true); let value = pair.0; // 0x42 let flag = pair.1; // true }
But in practice, nobody writes .0 and .1. The idiomatic approach is to unpack immediately.
Destructuring: the way you actually use tuples
#![allow(unused)] fn main() { let (result, carry) = self.v[x].overflowing_add(self.v[y]); }
overflowing_add returns (u8, bool). Destructuring pulls both values out in a single line and gives each a clear name. Compare this to the alternative:
#![allow(unused)] fn main() { let pair = self.v[x].overflowing_add(self.v[y]); let result = pair.0; let carry = pair.1; }
Same information, three times more noise. Destructuring is not just shorter — it makes the intent clearer at the point where the variable is introduced.
Returning multiple values from a function
Tuples are the standard way to return more than one value:
#![allow(unused)] fn main() { fn divide(a: u8, b: u8) -> (u8, u8) { (a / b, a % b) } let (quotient, remainder) = divide(10, 3); }
There is no "out parameter" in Rust. If a function needs to communicate multiple results, it packs them into a tuple (or a struct, if the values deserve names at the definition site).
The unit type: ()
An empty tuple () is called the unit type. It represents the absence of a meaningful value — the equivalent of void in other languages. Functions that do not explicitly return anything implicitly return ().
In the emulator: Tuples appear in the
8XY4(ADD with carry) and8XY5(SUB with borrow) opcodes, whereoverflowing_addandoverflowing_subreturn(value, bool). The bool is then stored in VF. See 8XY* — Arithmetic and Logic. The destructuring syntax used with tuples is explored further in Destructuring.