Integer Width Types
Shape’s primary integer type is int, a signed 64-bit integer. For C interop and
binary protocol work, the parser also recognizes width-specific integer type names
(i8, u8, i16, u16, i32, u32, u64).
The int Type (i64)
Section titled “The int Type (i64)”int is the default integer type. Unsuffixed integer literals are int. It is a
signed 64-bit integer (range: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807)
stored as a typed slot — the compiler proves the type at compile time and emits
typed opcodes that operate on raw native values without runtime tag checks.
let x = 42 // intlet y = -1000 // intlet big = 9000000000000000000 // int (within i64 range)print(x)print(y)print(big)For arbitrary-precision integers, use bigint. For floating-point values, use
number (f64).
Width-Typed Integers
Section titled “Width-Typed Integers”The following width-specific types are recognized in syntax:
| Type | Bits | Signed | Range |
|---|---|---|---|
i8 | 8 | Yes | -128 to 127 |
u8 | 8 | No | 0 to 255 |
i16 | 16 | Yes | -32,768 to 32,767 |
u16 | 16 | No | 0 to 65,535 |
i32 | 32 | Yes | -2,147,483,648 to 2,147,483,647 |
u32 | 32 | No | 0 to 4,294,967,295 |
u64 | 64 | No | 0 to 18,446,744,073,709,551,615 |
u64 is recognized for cases requiring unsigned 64-bit values (e.g., content
hashes, file sizes). Note that runtime integer storage is i64-backed: u64
values within the i64 range (0 to 9,223,372,036,854,775,807) round-trip
correctly, but values above i64::MAX are not yet safely representable at
runtime. Use bigint if you need the full unsigned 64-bit range. Other width
types are primarily useful as documentation in type annotations and for future
C interop.
Literal Suffixes
Section titled “Literal Suffixes”Append a type suffix to any integer literal to give it a specific width:
let a = 42i8 // i8let b = 255u8 // u8let c = 1000i16 // i16let d = 50000u16 // u16let e = 100i32 // i32let f = 3000000u32 // u32let size = 1048576u64 // u64print(f)print(size)Unsuffixed integer literals are int (i64). The suffix set is i8, u8, i16,
u16, i32, u32, u64 — there is no i64 suffix because unsuffixed literals
are already int (i64). Hex, binary, and octal prefixes combine with suffixes:
0xFFu8, 0b1010i16, 0o77u32.
Explicit Casting with as
Section titled “Explicit Casting with as”Use as to convert between integer types:
let wide: int = 300let narrow = wide as i8 // truncates to low 8 bits → 44
let signed: int = -1let unsigned = signed as u8 // reinterpret bits → 255print(narrow)print(unsigned)as performs a bit-level conversion: narrowing keeps the low bits (300 as i8
is 44), and signed/unsigned changes reinterpret the same bits (-1 as u8 is
255). It does not range-check or saturate.
In Type Definitions
Section titled “In Type Definitions”Width types can be used as field types in struct definitions, primarily as documentation of the intended storage semantics:
type Pixel { r: u8, g: u8, b: u8, a: u8}
type Sensor { id: u16, reading: i32, flags: u8}
let px = Pixel { r: 255u8, g: 128u8, b: 0u8, a: 255u8 }print(px.r)Best Practices
Section titled “Best Practices”- Use
intfor general-purpose integer work — it has full runtime optimization (typed slots, typed opcodes, JIT support). - Use
number(f64) when you need floating-point values. - Use
bigintfor arbitrary-precision integer arithmetic. - Use width types in type annotations when documenting binary protocol layouts or C interop signatures.
- Use
u64for unsigned values that stay within thei64range; reach forbigintwhen you need the full unsigned 64-bit range with guaranteed precision.