Special Types, Traits and Attributes
Builtin Types
Most types a game uses are ordinary declarations in std, written in Kagari like your own. A few
types are known to the compiler by name: they have their own syntax, their own checks, or no
visible representation at all. This chapter lists them and says what is special about each: whether
it is opaque, whether it is finite, whether it is Eq, in
which kinds it is useful, and what copying it means. The methods are
documented in the STD Reference.
Every type in Kagari has value semantics: assignment, passing an argument and returning copy the value (see Values, References and Closures). For the handle types below, the value that is copied is the handle, so both copies name the same stored thing.
Overview
| Type | Defined as | Finite | Eq | Typical kinds | STD page |
|---|---|---|---|---|---|
bool, () | language | yes | yes | all | — |
Int<lo, hi> | language | yes | yes | all | — |
u8 … u128, i8 … i128, usize, isize | language | no | yes | fn, const fn | — |
Rat, Decimal | #[builtin] struct | no | yes | fn, const fn | std |
String | #[builtin] struct | no | yes | fn, const fn | std |
Text | #[builtin] struct | yes | yes | all | std::narrative::text |
ImageAsset, AudioAsset, VoiceAsset, VideoAsset, FontAsset | #[builtin] structs | yes | yes | all | std |
ColorShader<A>, MixShader<A>, TextureShader<A>, MaskShader<A>, LocalMotionShader<A>, TransitionalMotionShader<A> | #[builtin] structs | no | yes | const fn | std::screen::shader |
Vec<T> | #[builtin] struct | no | if T: Eq | fn, const fn | std |
Range<T> | struct | if T is | if T: Eq | all | std |
Option<T>, Result<T, E> | enums | if the arguments are | if the arguments are | all | std |
Setting<T>, Meta<T> | #[builtin] structs | no | no | fn (see below) | std::system, std::narrative |
Narrative | #[builtin] struct | no | no | fn | std::narrative |
Screen | #[builtin] struct | no | yes | const fn | std::screen |
Entity, Prop<T>, ImageLayer, LabelLayer, MovieLayer, Hud, ChoiceMenu, Camera, Channel | #[builtin] structs | no | no | const fn | std::screen, std::screen::audio |
Change<T>, Swap, ChannelChange | #[must_use] #[builtin] structs | no | no | const fn | std::screen, std::screen::audio |
Element, Placed | #[builtin] structs | no | no | fn | std::ui |
Arm, Divergence | #[builtin] structs | no | no | fn | std::narrative, std::system::saves |
Render | type alias of const fn(Screen) -> Screen | yes | yes | all | std::screen |
Duration | struct | no | yes | all, as constants in the story | std::time |
| function pointers | language | all kinds but fn | yes | all | — |
closures Fn(…) | language | no | no | all; second-class in the story | — |
“Finite” decides whether a pushdown fn may hold a value of
the type at runtime (E3008). A constant expression of any type may still appear in the story,
because it is folded at compile time.
Opaque builtin types
A struct declared with #[builtin] is implemented by the compiler and
the engine. It has no visible fields:
- You cannot write a struct literal for it, read a field of it, or destructure it in a pattern.
Values come only from
stdfunctions and methods, from literals (Text,Decimal), or from the engine (Narrative). - It is
Eqonly whenstdsays so with animpl Eq. The structuralEqrule does not look inside it (see Language Traits). - It is finite only if it is
Textor an asset type. Every other builtin type counts as infinite. - Its representation is never part of the
.kagarifile, so it can change between engine versions without breaking games.
Only std may declare builtin types (E1008). Most of them are in the prelude; the screen state
types (Entity, Prop<T>, the layers, …) are not, because you receive them as &mut references
from Screen methods and rarely name them.
Text
Text is constant text: plain ("Mio") or rich (built by t![…], with
ruby, text effects and interpolation). A string literal has type Text, not String.
const NAME: Text = "Mio";- Finite. The values of
Textin the story are the constant texts of the program, so a story variable, parameter or return value of apushdown fnmay beText. - Not in entry scenes.
fncode can build new text at runtime withto_text, so the parameter and return types of an entry scene must not containText(E3009). - Interpolation.
{expr}insidet![…]is a call ofto_text(expr), which needsexpr: TwithT: ToText. In apushdown fn,to_textof a finite runtime value is tabulated like anyconst fncall, so the line stays a finite function of the story state. Eq: yes.
String
String is a runtime string for fn code. It is not finite and is Eq and ToText.
Tentative. The current std declares no function that creates or changes a String; text a
game shows is Text.
Asset types
ImageAsset, AudioAsset, VoiceAsset, VideoAsset and FontAsset name files packed with the
game, and so do the six shader types (ColorShader<A> and the others), which name WGSL files
(.wgsl) of one role with arguments of type A. A value is created only by a constant call of
asset or system_font:
const CG_001: ImageAsset = asset("cg_001.png");const THEME: AudioAsset = asset("bgm/theme.ogg");const GOTHIC: FontAsset = system_font("msgothic.ttc");- The type argument of
asset::<A>is inferred from the constant’s type and must implementAsset. - The argument must be a string literal (
E2028), not empty (E1028), inside the asset directory (E1029) and outside its reserved top-level directoriessystem-fontsandstd(E1032); a missing file isE1027.system_fonthas its own rules (E1030,E1031); see the STD Reference,std. - Finite. The values of a file asset type are exactly the asset constants of the program, so the story may hold them, and a misspelled asset is a name-resolution error instead of a runtime failure. The shader types are not finite; a shader is used by renders, not by the story.
Eq: yes.ShaderAsset, the role-less identity of a shader file that effect values hold, is notAssetand not in the prelude; games do not name it. Seestd::screen::shader.
Setting<T> and Meta<T>
A setting and a meta field are persistent global values stored outside saves. Each is declared by
an immutable static that holds a handle:
static MUSIC: Setting<Volume> = setting(volume(60));static CLEARED_AZUSA: Meta<bool> = meta(false);- An immutable
staticmust hold aSetting<T>or aMeta<T>(E3011), andsetting(…)andmeta(…)may be called only as the initializer of astatic(E4007). The initializer gives the value used when the stored file has none. - The handle itself is a value: it can be passed to functions and stored in records such as
Settings. Copying it copies the handle; both copies read and write the same stored value. - Kinds.
getandsetarefn.Meta::writeis apushdown fn: the story writes meta and never reads it. Aconst fnmay use aSettinghandle only as a value, as when passing it toEntity::gate; callinggetthere isE3001.constcode may not mention a meta field at all (E3013). - Not finite and not
Eq.
See The Game System and Settings in renders.
Narrative
Narrative is the system’s only view of a running story. The engine passes one to the game view
(System::game, a fn(Narrative) -> Element) and to game overlays (fn(Narrative) -> GameOverlay),
and nowhere else. Its representation is hidden, so system code cannot read story variables. All
its methods are fn, and its reads are reactive. A copy names the same running game. Not finite,
not Eq.
Screen and its parts
Screen is the screen a step shows together with the step’s timeline. It is the input and output
of every render. All its methods are const fn.
- Value. A render receives a copy of the previous screen and returns a new one; changing the copy never changes the caller’s screen.
- Parts.
Entity,Prop<T>,ImageLayer,LabelLayer,MovieLayer,Hud,ChoiceMenu,CameraandChannelare parts of a screen. You get them as&mutreferences from methods such asscreen.entity(id)orscreen.music(); a change through such a reference changes that screen. - Pending changes.
Change<T>,SwapandChannelChangeare pending changes returned bychange,clearandstop. They are#[must_use]: nothing happens untilapply_animationis called, and a pending change that is dropped is warningW3023. ScreenisEq, so the compiler can compare screens for consistency. The parts are notEq. None of them is finite.
Element and Placed
Element is a UI element tree, laid out by the engine; Placed is an element positioned inside a
layer![…]. Both are built by the UI forms and std::ui functions, which are all fn. Not finite,
not Eq.
Arm and Divergence
Arm is a selectable arm of the current choice, read from Narrative::menu and passed back to
Narrative::select. Divergence is the error of nav::load when a save no longer fits the
program. Both exist only in fn code. Not finite, not Eq.
Numbers
| Type | Defined as | Finite | Notes |
|---|---|---|---|
Int<lo, hi> | language primitive | yes | The integers lo..=hi; lo and hi are constant i64 values with lo <= hi (E2004). The only integer type the story may hold at runtime |
u8 … u128, i8 … i128, usize, isize | language primitives | no | Rust’s integers with Rust’s operators |
Decimal | #[builtin] struct in std | no | Exact decimal; a literal with a decimal point (0.5) is a Decimal. The replacement for floats |
Rat | #[builtin] struct in std | no | Exact rational; no literal, written Rat::new(num, den) |
All of them are Eq; integers, Rat and Decimal are ordered (<, <=, …). Integer types never
mix in arithmetic (E2014) and convert only through the Integer methods
into and saturate; there are no as casts (E2019). Arithmetic, literal typing, saturation and
overflow are described in Numbers.
Vec<T>
Vec<T> is a growable list for fn and const fn code. It is a value like every type: push and
set change the place they are called on, and copies are unaffected.
v[i]indexes it with any integer type, andfor x in viterates it; both are built into the language forVec<T>and arrays.getout of range returnsNone;setout of range is an evaluation error (E4005during compile-time evaluation).- Not finite.
EqwhenT: Eq.
Range<T>
Range<T> is an ordinary std struct with public fields start, end and inclusive. The
compiler knows it in two places:
- Range expressions.
a..banda..=bbuild aRange<T>, whereTis a type both bounds convert to:0..7is aRange<Int<0, 7>>. Onlya..banda..=bare range values; other forms such asa..,..bor..areE2030. Patterns have their own range forms (a..,..=b,a..b,a..=b); see Range patterns. forloops.foriterates aRange<T>whoseTis an integer type (E2031otherwise). In apushdown fn, the bounds must be constant expressions (E3006).
Option<T> and Result<T, E>
Both are ordinary generic enums of std, and the prelude imports their variants None, Some,
Ok and Err. The compiler knows them for the ? operator: on an Option it needs a function
that returns Option, and on a Result one that returns Result with the same error type
(E2034). There is no error conversion. They are finite and Eq when their type arguments are.
Function pointer and closure types
Function types carry the kind of the code they call, so the call rules hold through pointers and closures too.
| Type | Holds | Finite | Closure counterpart |
|---|---|---|---|
fn(A) -> B | a named fn | no | Fn(A) -> B |
const fn(A) -> B | a named const fn, or a closure that captures nothing | yes | const Fn(A) -> B |
pushdown fn(A) -> B | a named instant procedure | yes | pushdown Fn(A) -> B |
scene fn(A) -> B | a named scene | yes | none |
Fn(A) -> B, const Fn(A) -> B, pushdown Fn(A) -> B | a closure or function of that kind | no | — |
- A named function coerces to a pointer or closure type of exactly its kind. A closure that captures
nothing coerces to a pointer of its kind; a capturing one cannot (
E2036). A pointer coerces to the closure type of the same kind. pushdownandsceneare keywords only directly beforefn(andpushdownbeforeFn).- Finite. A pointer of a story or
constkind is finite, because its values are the finitely many named functions of the program. Closures are never finite; in the story they are second-class. Eq. Function pointers compare by identity. Closures are neverEq(E2017).
Render
pub type Render = const fn(Screen) -> Screen;Render, in std::screen and the prelude, is an alias, not a new type. A render is a const fn
from the screen before a step to the screen after it; steps take one as their render attribute.
Because it is a const fn pointer, Render is finite and Eq, and a non-capturing closure can be
written inline wherever a Render is expected. See
Render functions.
const fn clear_background(mut screen: Screen) -> Screen { screen.background().clear().apply_animation(fade(ms(500))); screen}
const CLEAR: Render = clear_background;Duration
pub struct Duration { pub ms: u32 }Duration is an ordinary struct of std::time, not a builtin type. It is listed here because
every time in std is a Duration of whole milliseconds: there are no unit strings and no frame
counts. The prelude has Duration, ms(n) and secs(n).
- It holds a
u32, so it is not finite: the story uses durations only in constant expressions, such asfade(ms(500))inside a render. Eq: yes, structurally.divrounds down:ms(1000).div(3)isms(333). A duration that does not fit inu32milliseconds, whether built bysecs, byplusortimes, or reached by a screen’s timeline, isE4013during compile-time evaluation.
Last updated Oct 29, 2026