Type Casting

Rust never converts types implicitly. Every conversion must be written explicitly with the as keyword. This makes intent visible and prevents silent bugs from unexpected coercion.

Basic syntax

#![allow(unused)]
fn main() {
let x: u16 = 0x0A;
let as_usize = x as usize;
let as_u8    = x as u8;
}

Why array indexing needs usize

Array indices must be usize — the platform-native integer for memory sizes. The compiler rejects any other type:

#![allow(unused)]
fn main() {
let x: u16 = 5;
self.v[x]           // ERROR: expected usize, found u16
self.v[x as usize]  // OK
}

Casting to a smaller type: truncation

When casting to a smaller type, the upper bits are discarded — only the lower bits remain:

#![allow(unused)]
fn main() {
let value: u16 = 0x6A42;
let low = value as u8;  // keeps only the lower 8 bits → 0x42
}
0x6A42  =  0110 1010  | 0100 0010
                        ─────────
                        as u8 → 0x42

This is exactly what happens when extracting the NN byte from an opcode:

#![allow(unused)]
fn main() {
let nn = (opcode & 0x00FF) as u8;
}

The mask isolates the lower byte; the cast keeps it as u8.

Casting to a larger type: zero extension

Zeros are added to the left:

#![allow(unused)]
fn main() {
let value: u8  = 0x42;
let extended   = value as u16;  // 0x0042
}

This is what happens during fetch when combining two bytes into a 16-bit opcode:

#![allow(unused)]
fn main() {
let high = ram[pc] as u16;       // 0xAB → 0x00AB
let low  = ram[pc + 1] as u16;   // 0xCD → 0x00CD
let opcode = (high << 8) | low;  // 0xABCD
}

Without casting to u16 before shifting, the shift would overflow the 8-bit boundary.

Summary

ConversionWhat happens
small → largezeros added on the left
large → smallupper bits discarded
any → usizerequired for array indexing

In the emulator: Type casting appears in nearly every opcode. The register index extracted from an opcode (u16) must be cast to usize before indexing into self.v[x]. Values loaded into registers must be cast to u8. See the Bitwise Operations chapter for the masking operations that precede most casts, and The First Opcode: JP for the full fetch cycle where both casting and shifting are needed together.