Types
Primitive Types
| Type | Description |
|---|---|
bool | Boolean: true or false |
u8, u16, u32, u64 | Unsigned integers |
i8, i16, i32, i64 | Signed integers |
usize, isize | Pointer-sized integers |
f32 | 32-bit float |
char | Unicode scalar value |
string | Immutable UTF-8 text |
never | The type of expressions that do not return |
() | Unit type (nothing / void) |
There is no
null. Absence is represented withOption<T>.
f64is intentionally excluded from Flint 1.0. Usef32for floating-point work.
Numeric Literals
let a: u32 = 1_000_000 // decimal with separator
let b: u8 = 0xFF // hex
let c: u8 = 0b1111_0000 // binary
let d: u8 = 0o177 // octal
let e: f32 = 1.5e-3 // scientific notation
let f: u32 = 100u32 // explicit type suffix
Integer literals without a suffix are typed from context. If there is no context, they default to i32. Float literals default to f32.
Integer Rules
Signed and unsigned integers are distinct types. The compiler never implicitly converts between them.
let a: u8 = 200
let b: i8 = a // error: type mismatch
// Explicit conversion:
let b = i16.from(a) // infallible (u8 -> i16 is always safe)
let b = i8.try_from(a) // fallible -> Option<i8>
Overflow behavior:
- Debug builds: overflow traps
- Release builds: overflow wraps
wrap_add,wrap_sub,wrap_mulexist incore/*for explicit wrapping
Division by zero always traps.
Compiler-Known Generic Types
These types are built into the language. You cannot define new generic types, but you can use these:
| Type | Description |
|---|---|
Option<T> | Optional value: Some(T) or None |
Result<T, E> | Success or failure: Ok(T) or Err(E) |
List<T> | Growable contiguous sequence |
Deque<T> | Double-ended queue |
Map<K, V> | Key-value map |
Set<T> | Unique value set |
slice<T> | Read-only view over contiguous data |
mut slice<T> | Exclusive mutable view over contiguous data |
These cover the overwhelming majority of what user-defined generics are used for in other languages. Go added generics after a decade and the community still largely reaches for concrete types and interfaces instead. You will probably be fine.
Arrays and Slices
Fixed-size arrays:
let buf: [u8; 8] = [0; 8] // 8 zeros
let rgb: [u8; 3] = [255, 0, 128]
buf[0] = 42 // indexing requires usize
let view = buf[1..4] // produces slice<u8>
Rules:
- Array length must be a compile-time constant.
- Out-of-bounds access at runtime triggers a hard fault trap on MCU targets.
slice<T>is a read-only view;mut slice<T>is an exclusive mutable view.- Slices do not own data.
Strings
let greeting: string = "Hello, Flint!"
let newline = '\n' // char literal
let byte: u8 = b'A' // byte char literal -> u8
let raw = """
This is a raw multiline string.
Escapes like \n are not processed here.
"""
let bytes: slice<u8> = b"binary data\x00" // byte string
Rules:
stringis immutable. You cannot modify it in place.- No integer indexing into
string. Use iterators or string APIs. charis a Unicode scalar value (one code point).string.len()returns the UTF-8 byte length.string.bytes()returns an immutableslice<u8>view.- Read-only and view-returning text methods live on
string. - Mutable text lives in
std/text.Buffer[N].
See String and Char for the dedicated text chapters.
Type Aliases
alias Bytes = slice<u8>
alias BytesMut = mut slice<u8>
alias Handler = (i32, i32) -> bool
Aliases are not new types. They are just shorthand for an existing type. Alias names follow CamelCase.
Collection Algorithms
Flint 1.0 uses explicit eager collection methods on sequential collections. These helpers are available on fixed arrays, slice<T>, and List<T>.
Searching
Search helpers return indices, not element values:
let temps: List<i32> = [68, 72, 81, 79, 81]
let first_hot = temps.find(|value| {
return value > 80
}) // Option<usize>, Some(2)
let first_81 = temps.index_of(81) // Option<usize>, Some(2)
let last_81 = temps.last_index_of(81) // Option<usize>, Some(4)
let last_hot = temps.find_last(|value| {
return value > 80
}) // Option<usize>, Some(4)
Transforming
map and filter allocate a new List and leave the source collection unchanged:
let nums: List<i32> = [1, 2, 3, 4, 5]
let squares = nums.map(|value| {
return value * value
}) // List<i32>
let evens = nums.filter(|value| {
return value % 2 == 0
}) // List<i32>
Reducing
reduce combines the collection into a single value:
let nums: List<i32> = [1, 2, 3, 4, 5]
let total = nums.reduce(0, |sum, value| {
return sum + value
}) // 15
Sorting
sort_by sorts a mutable List<T> in place:
use core/cmp
let mut nums: List<i32> = [5, 3, 1, 4, 2]
nums.sort_by(|a, b| {
return cmp.compare(a, b)
})
Membership Operator
let has_three = 3 in nums // true
let has_key = "PORT" in config // Map key lookup
if "admin" in roles {
grant_access()
}
Supported for fixed arrays, slice<T>, List<T>, Set<T>, and Map<K, V> key lookup. String membership is also supported for both char in string and string in string.