ajhahn.de
← all chapters

Flash tutorial · 4 / 12

4. Variables & Constants

In Flash, values are declared with explicit mutability control. You can declare values using var, const, or the short-hand initialization operator :=.


1. Variable Declarations (var)

Variables declared with var are mutable; their values can be modified after initialization.

  • With Explicit Type: Specify the type between the name and the = operator.
  • With Inferred Type: Omit the type, and the compiler will infer it from the right-hand side expression.
// Explicit type
var count i32 = 0
count = count + 1 // OK

// Inferred type
var factor = 2.5
factor = factor * 2.0 // OK

2. Constant Declarations (const)

Constants declared with const are immutable. Once initialized, their value cannot be changed.

  • Like variables, you can either provide an explicit type or let the compiler infer it.
// Explicit type
const MAX_BUFFER usize = 1024

// Inferred type
const greeting = "Hello, Flash!\n"

// MAX_BUFFER = 512 // Compiler error downstream!

3. Short-hand Declaration (:=)

For quick bindings where you want the type to be inferred, Flash provides the := short-hand syntax.

Important: Short-hand declarations using := are immutable (constant) by default. They are a convenient syntactic sugar for const name = expression.

// Syntax: name := expression (equivalent to const name = expression)
msg := "hello\n"   // Inferred as constant string slice []const u8
// msg = "world"   // Compiler error downstream!

[!NOTE] := is the canonical spelling for an untyped immutable local. Running flashc fmt rewrites an untyped const name = expr into the name := expr short-hand automatically, so formatted Flash uses := consistently. A const that carries an explicit type — const MAX usize = 1024 — is left exactly as written.


4. Destructuring

A tuple value — most often a multi-value return (see Chapter 5) — unpacks into several names in one statement. One keyword rules all the names: := declares every target immutably, var … = declares every target mutably.

// Both names declared immutably
q, r := divmod(7, 2)

// Both names declared mutably
var x, y = pair()

// _ skips a position you do not need
tok, _ := next()

Existing lvalues — including member and index targets — take a destructuring assignment with plain =:

x, arr[0] = pair()

A few rules keep the form unambiguous: a := target must be a plain name (assign to members or indices with =), a compound operator like += cannot destructure, and a destructure that discards every position is written _ = expr instead.


5. Undefined Values

In low-level systems programming, you often want to allocate space for a variable without spending CPU cycles initializing it. Flash allows you to initialize a variable as undefined.

use flibc

export fn main(_ usize, _ argv) noreturn {
    // Allocate 512 bytes on the stack without initializing
    var buffer [512]u8 = undefined
    
    // Fill it via a read syscall
    _ = flibc.sys.read(0, &buffer, buffer.len)
    
    flibc.exit()
}

[!WARNING] Accessing a variable initialized to undefined before writing to it results in undefined behavior. Use with care.


6. Alignment & Section Placement (align, linksection)

A binding can pin the alignment of its storage with align(N), written between the type and the =:

// A page-aligned buffer — valid at file scope and inside functions
var pool [4096]u8 align(4096) = undefined

The form works on locals and on file-scope bindings alike, extern var included (the alignment becomes part of the symbol's contract).

A file-scope binding can also name the linker section its symbol lands in with linksection("…"), in the same slot (after any align):

// Pin read-only data where the linker script expects it
const PAD [4096]u8 linksection(".rodata") = [_]u8{0} ** 4096

Two boundaries: an extern var cannot carry a linksection (the defining object owns the section), and a local binding cannot either (a stack slot lives in no section). Functions take the attribute too, between the parameter list and any callconv(…).