Flash tutorial · 10 / 12
10. Modules & Imports
Flash projects are modular. Source files can import system modules, sibling files, and re-export declarations to compile into cohesive binaries.
1. Importing Modules (use)
To import a module, use the use statement at the top level of your file.
// Import flibc module
use flibc
Import Aliases (as)
If a module name is long, or conflicts with another declaration, you can rename it during import:
// Import syscall_defs and reference it as 'defs'
use syscall_defs as defs
pub const Dirent = defs.Dirent
2. Importing Sibling Files
To import another source file in the same directory, name the file stem in double quotes. The quoted name carries no extension — the compiler adds the backend suffix (.zig) during lowering:
// Import a local Zig wrapper file
use "syscalls" as sys
3. Re-exporting (pub use)
A Flash module can act as an import hub for other files. To make an imported file accessible to external modules that import this module, use pub use.
This translates directly to public constant imports downstream:
pub use "io" as io translates to pub const io = @import("io.zig") in Zig — the extension appears only in the lowered output.
Here is a simplified example of how flibc.flash acts as a re-export hub for userland:
// flibc.flash
pub use "syscalls" as sys
pub use "io" as io
pub use "heap" as heap
// Expose standard functions directly at the top level
pub const printf = io.printf
pub const malloc = heap.malloc
pub const free = heap.free
Now, any other program can simply use flibc and reach these functions:
// main.flash
use flibc
export fn main(_ usize, _ argv) noreturn {
ptr := flibc.malloc(64)
defer flibc.free(ptr)
flibc.printf("Allocated memory!\n")
flibc.exit()
}
4. Sharing Symbols Across Objects (export var / extern var)
Beyond functions, file-scope variables can cross object boundaries — the kernel symbol-sharing pattern, where one module owns the storage and every other object names it:
// In the module that OWNS the storage: define a cross-object symbol
export var nr_tasks i32 = 0
// In every module that CONSUMES it: typed, with no initializer —
// the storage is defined elsewhere, like an extern fn prototype is bodyless
extern var nr_tasks i32
pub composes in front (pub export var, pub extern var). An extern var carrying an initializer, or missing its type, is rejected with a targeted diagnostic.
The form also covers linker-script symbols, where the address itself is the value:
extern var _kernel_pa_end u8