Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Types

Primitive Types

TypeDescription
boolBoolean: true or false
u8, u16, u32, u64Unsigned integers
i8, i16, i32, i64Signed integers
usize, isizePointer-sized integers
f3232-bit float
charUnicode scalar value
stringImmutable UTF-8 text
neverThe type of expressions that do not return
()Unit type (nothing / void)

There is no null. Absence is represented with Option<T>.

f64 is intentionally excluded from Flint 1.0. Use f32 for 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_mul exist in core/* 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:

TypeDescription
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:

  • string is immutable. You cannot modify it in place.
  • No integer indexing into string. Use iterators or string APIs.
  • char is a Unicode scalar value (one code point).
  • string.len() returns the UTF-8 byte length.
  • string.bytes() returns an immutable slice<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.