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

Syntax Style

Flint’s syntax is brace-based, semicolon-free, and deliberately small. If you know Go, Python, or Rust, most of it will feel immediately familiar.

General Rules

  • Braces delimit blocks. Indentation is style, not syntax.
  • No semicolons in normal code. A newline ends the current statement.
  • No semicolons means no ambiguity workarounds: open delimiters ((, [, {) extend a statement across lines naturally.
  • Expression-oriented only where it genuinely simplifies things.

Automatic Statement Termination

A newline ends the current statement unless:

  • The parser is inside an open (, [, or {
  • The line ends with an infix operator or incomplete assignment
  • The line ends with .

This means multiline calls and literals work without any special syntax:

let result = some_function(
    first_arg,
    second_arg,
    third_arg,
)

let point = Point {
    x: 10,
    y: 20,
}

Naming Conventions

Flint has four naming styles. Violations are reported as errors by flint check and corrected automatically by flint fmt:

Ordinary identifiers are ASCII-only. They start with a letter, may use digits or _ after the first character, and may not end with _. Bare _ is reserved for discard and wildcard use.

ItemStyleExample
Functions, methods, variables, fields, modulessnake_caseread_bytes, retry_count
Types, traits, type aliases, enum variantsCamelCaseSerialPort, ParseError, Some
ConstantsUPPER_SNAKE_CASEMAX_RETRIES, LED_PIN
Import pathssnake_case with /micro/gpio, std/time

Even when a convention uses internal _ separators, ordinary names still must begin with a letter and may not end with _.

use micro/gpio
use std/time

const LED_PIN: u32 = 25

struct SerialPort {
    baud: u32
    tx_pin: u32
}

fn read_bytes(mut port: SerialPort) -> Result<List<u8>, Error> {
    // ...
}

Comments

// Single-line comment

/// Doc comment (used for documentation generation)
/// @deprecated use=std/time.sleep_ms since=1.2
pub fn delay_ms(ms: u32) { ... }

Block comments are not part of Flint source syntax. Use // on multiple lines.

Operators

Boolean

Flint uses English-like boolean operators instead of symbols:

OperatorMeaning
andlogical AND (short-circuits)
orlogical OR (short-circuits)
notlogical NOT
inmembership test
if is_ready and not is_busy {
    send(packet)
}

if value in valid_range {
    process(value)
}

There is no &&, ||, !, or ?: ternary.

Arithmetic

OperatorMeaning
+addition
-subtraction
*multiplication
/division
%remainder
let cycles = freq * duration / 1000
let offset = (index % buffer_size) + base

Integer overflow is a compile-time error where detectable, and a checked trap at runtime otherwise. There is no silent wraparound.

Bitwise

OperatorMeaning
&bitwise AND
|bitwise OR
^bitwise XOR
~bitwise NOT
<<left shift
>>right shift
let mask: u32 = 0b0000_1111
let flags = status & mask
let shifted = value << 4
let inverted = ~flags

These operators work on integer types only. Bit manipulation is common in firmware and Flint treats it as a first-class operation with no surprises.

Assignment

OperatorMeaning
=assign
+=add and assign
-=subtract and assign
*=multiply and assign
/=divide and assign
%=remainder and assign
&=bitwise AND and assign
|=bitwise OR and assign
^=bitwise XOR and assign
<<=left shift and assign
>>=right shift and assign
count += 1
flags |= ENABLE_BIT
buffer_pos %= buffer_size

There is no ++ or --. Use += 1 and -= 1 instead. Increment and decrement operators have a long history of subtle bugs around sequencing and expression context. The explicit form is unambiguous.

No Operator Overloading

Operators always mean exactly what they say. There is no way to redefine +, -, or == for custom types. Use named methods instead:

// Do this
let result = vec.add(other)

// Not this (not possible in Flint)
let result = vec + other