Module static storage¶
static gives a module its own runtime storage cell.
Use it when state must:
- live for the lifetime of the program
- be shared by multiple functions in the same module
- remain shared when exported via
pub static
If that is not what you need, static is the wrong tool.
Mental model¶
A static is not “a mutable const”.
It is closer to:
- a module-owned variable with a stable identity
- initialized once in declaration order when the module is initialized
- read and written through compiler-managed access rules
Every read observes the current contents of that storage cell. If another function mutates the static, later reads see the updated value.
static counter: int = 0
def next_id() -> int:
counter += 1
return counter
next_id() does not recompute counter. It updates the same module-owned storage each time.
When to use static¶
Reach for static when the module owns long-lived runtime state such as:
- counters
- registries
- caches
- accumulated diagnostics or metrics
- shared mutable collections
static registered_names: list[str] = []
def register_name(name: str) -> None:
registered_names.append(name)
def count_names() -> int:
return len(registered_names)
When not to use static¶
Do not use static just because:
- a value is “important”
- a value is used in many places
- a value should not be reassigned
Those are usually const or plain module helper values.
Use:
constfor compile-time, deeply immutable data- function parameters / return values for short-lived state flow
- models/classes for explicit state carried by an object
const vs static¶
The distinction is semantic:
constis compile-time and deeply immutablestaticis runtime-initialized and live
const API_VERSION: str = "v1"
static request_count: int = 0
API_VERSION is fixed baked data.
request_count changes as the program runs.
See also: Const bindings
Aliases are still live¶
Direct aliases created from a static still refer to the live stored value.
static items: list[int] = []
def add_pair() -> None:
let live_items = items
live_items.append(1)
live_items.append(2)
After add_pair(), items contains both values because live_items refers to the same live storage-backed value.
That is intentional. static exists to model module-owned state, not one-time snapshots.
Exporting shared state¶
Use pub static when another module must observe or mutate the same storage cell:
pub static hits: int = 0
pub def record_hit() -> None:
hits += 1
from counters import hits, record_hit
def main() -> None:
record_hit()
record_hit()
println(hits)
This prints 2.
hits in main.incn is not a copy. It refers to the same shared module storage declared in counters.incn.
Boundaries and constraints¶
static is intentionally narrow because it represents module-owned state, not general-purpose hidden globals.
That is why the language keeps it at module scope and gives it explicit declaration rules instead of making it a more casual variation of let.
For the exact syntax, initialization, and error rules, see: Static storage (reference)
Design intent¶
Incan prefers explicit state ownership.
static exists for the cases where the owner really is the module itself.
That makes module-level caches, counters, and registries possible without pretending they are compile-time constants.