Types
Primitive Types
Integer Types
| Type | Size | Range |
|---|---|---|
i8 | 8-bit | -128 to 127 |
i16 | 16-bit | -32,768 to 32,767 |
i32 | 32-bit | -2^31 to 2^31-1 |
i64 | 64-bit | -2^63 to 2^63-1 |
isize | pointer-sized | Platform dependent |
u8 | 8-bit | 0 to 255 |
u16 | 16-bit | 0 to 65,535 |
u32 | 32-bit | 0 to 2^32-1 |
u64 | 64-bit | 0 to 2^64-1 |
usize | pointer-sized | Platform dependent |
Integer literals default to i32. Use suffixes for other types: 42u8, 100i64, 0usize.
Floating-Point Types
| Type | Size | Precision |
|---|---|---|
f16 | 16-bit | ~3 decimal digits (IEEE 754 half-precision) |
bf16 | 16-bit | ~3 decimal digits (Brain Float, ML workloads) |
f32 | 32-bit | ~7 decimal digits |
f64 | 64-bit | ~15 decimal digits |
Float literals default to f64. Use suffix for f32: 3.14f32.
f16 and bf16 are half-precision types for ML inference and signal processing. bf16 has the same exponent range as f32 but fewer mantissa bits — ideal for neural network weights. Energy cost: 0.4 pJ per operation (vs 0.35 pJ for f32).
Boolean
let a: bool = true;
let b: bool = false;
Character
let c: char = 'A'; // Unicode scalar value
let emoji: char = '\u{1F600}';
Unit Type
The unit type () represents the absence of a meaningful value. Functions without a return type return ().
Compound Types
Tuples
Fixed-length, heterogeneous sequences:
let pair: (i32, String) = (42, "hello");
let (x, y) = pair; // destructuring
let first = pair.0; // field access
Arrays
Fixed-length, homogeneous sequences:
let arr: [i32; 5] = [1, 2, 3, 4, 5];
let zeros = [0; 10]; // 10 zeros
let first = arr[0]; // indexing
Slices
Dynamically-sized views into arrays:
let slice: &[i32] = &arr[1..3];
String Types
String
Owned, heap-allocated, growable UTF-8 string:
let s: String = "Hello, world!";
let greeting = "Hi " + name; // concatenation
let len = s.len(); // byte length
&str
Borrowed string slice:
let s: &str = "literal";
Union Types
Union types allow a value to be one of several types. They are declared with the | separator:
type Number = i32 | i64 | f64;
type JsonValue = i64 | f64 | String | bool | Vec<JsonValue>;
type Result = Data | ErrorCode;
Union types are matched exhaustively:
fn describe(val: Number) -> String {
match val {
x: i32 => format!("i32: {}", x),
x: i64 => format!("i64: {}", x),
x: f64 => format!("f64: {}", x),
}
}
Union Type Rules
- Each constituent type must be distinct
- The compiler tracks which variant is active at runtime via a discriminant tag
- Pattern matching on union types is exhaustive -- all variants must be handled
- Union types compose:
type A = B | CwhereBandCcan themselves be union types
Generic Types
Vec
Dynamic array:
let mut v: Vec<i32> = Vec::new();
v.push(1);
v.push(2);
let first = v[0];
Option
Optional value:
let some: Option<i32> = Option::Some(42);
let none: Option<i32> = Option::None;
Result<T, E>
Fallible operation:
let ok: Result<i32, String> = Result::Ok(42);
let err: Result<i32, String> = Result::Err("failed");
Box
Heap-allocated value:
let boxed: Box<i32> = Box::new(42);
HashMap<K, V>
Key-value map:
let mut map: HashMap<String, i32> = HashMap::new();
map.insert("key", 42);
Smart Pointers
See Smart Pointers for full documentation.
let rc = Rc::new(42); // single-threaded shared ownership
let arc = Arc::new(42); // thread-safe shared ownership
let cow = Cow::borrowed("hi"); // clone-on-write
Const-Generic Types
// SmallVec — inline buffer with heap spillover
let mut sv: SmallVec[i32; 8] = SmallVec::new();
sv.push(42); // stored inline (no allocation until > 8 elements)
// Simd — portable SIMD vectors
let v: Simd[f32; 4] = Simd::splat(1.0);
let w: Simd[f32; 4] = Simd::from_array([1.0, 2.0, 3.0, 4.0]);
let sum = v.add(&w);
See Simd for full SIMD documentation.
N-Dimensional Arrays
See NDArray for full documentation.
let mat: NDArray[f64; 2] = NDArray::zeros([3, 4]);
let view: NDView[f64; 1] = mat.row(0);
Type Aliases
pub type Token = Spanned<TokenKind>;
pub type ParseResult<T> = Result<T, ParseError>;
Type Inference
The compiler infers types when possible:
let x = 42; // inferred as i32
let v = Vec::new(); // type inferred from usage
v.push(1u8); // now inferred as Vec<u8>
Explicit annotations are required when the type cannot be inferred from context.
Type Casting
Use as for numeric conversions:
let x: i32 = 42;
let y: f64 = x as f64;
let z: u8 = x as u8; // truncation
let p: usize = x as usize;