Lexical structure
The character-level rules of rut source: files, comments, identifiers, the keyword set, and the enforced naming conventions.
Source model
- Files are UTF-8, extension
.rut. Both LF and CRLF line endings are accepted (CRLF is normalized); a leading BOM is skipped. - Comments:
// line comment/* block comment */— block comments do not nest/// doc comment(also/** ... */) — attaches to the following declaration and is kept for tooling; it is not semantic
- There is no off-side rule: statements end with
;(required), blocks are braced.
Identifiers and $
ident := (letter | "_" | "$") (letter | digit | "_" | "$")*
$ is an ordinary identifier character, valid anywhere in a name:
on_mount$, viewport_size$, $temp, $x1, even $ alone. There is no
$ punctuation token and no $-interpolation (f-string holes use
{ } — see Literals and inference).
The trailing $ is a naming convention, not a lexical category: it
marks dispatch-inverted members — anything a runtime fires or owns
rather than you calling it. Event props (on_click$), lifecycle hooks
(on_mount$, before_destroy$), subscription updates (on_update$),
watch controls (start$, stop$), engine atoms (viewport_size$), and
mutation handles generally. Never on functions you call, widget
builders, or types.
Keywords
The keyword set is exactly:
fn | let | mut | if | else |
while | for | of | return | when |
enum | struct | class | trait | impl |
requires | use | pub | static | async |
await | extern | is | host | select |
true | false | nil |
Contextual words — ordinary identifiers elsewhere:
| Word | Special meaning |
|---|---|
type | the alias introducer (type Km = Meters; — see Type aliases and union bounds) |
entry | entry fn at module scope publishes the function to the embedder |
self | the explicit receiver, first parameter of an instance method |
Self | names the enclosing class inside its body; the class-private literal Self { .. } |
new | not special — the conventional construction-method name (Rect.new(..)); there is no new expression |
as | the numeric cast (x as u32) and the select arm bind |
super | only inside pub(super) |
builtin | declaration modes of the engine’s own surface (builtin fn, builtin primitive, builtin trait, builtin impl) |
panic(msg) and assert(cond, msg?) are prelude functions, not keywords.
Reserved words
Each reserved word is a hard error whose message names the rut replacement:
| Reserved | Error replacement |
|---|---|
switch, case, match | when — arms are pattern -> body |
null, undefined, void | absence is nil on a ?T |
any | a trait type or opaque |
typeof, instanceof | x is T tests at runtime |
extends | no inheritance — compose instead |
interface | rut spells this trait |
dataclass | removed — spell it struct |
delete | no dynamic properties |
in | iteration is for (let x of ..) |
with | — |
var, const | bindings spell let / let mut |
private | members are private by default; add pub |
Naming conventions (enforced)
- Types are PascalCase — user types and parameterized builtins:
Vec<T>,[T],Weak<T>,opaque,Task<T>,Point,Drawable. Scalars and simple buffers stay lowercase:i32,u8,f32,bool,str,bytes. - Functions and methods are lower_snake_case —
unwrap_or(d),push(v),checked_add(y),spawn_worker(..). - Construction is a method call, never a type-call. User classes
construct through their own class methods:
Rect.new(3, 4),Rect.from(other),Version.parse(s)— see Classes and constructors. Only builtin surfaces keep call forms:bytes.zeroed(n),Weak(v),opaque(v), the repeat[v; n], andVec<T>.from(..)(see Builtin generic types). - The trailing-
$marker is kept meaningful by this style rule alone; the compiler attaches no semantics to it.