Eine Variable in Lyx ist immer drei Entscheidungen zugleich: Speicherklasse (darf sie sich ändern?), Typ (was steht drin?) und Ort (lokal oder auf Modulebene?). Diese Seite geht den Lebenszyklus durch — Deklaration, Initialisierung, Gültigkeit, Lebensdauer — und sammelt die Stellen, an denen der Compiler heute anders reagiert, als man erwartet.
→ Syntax-Referenz · Datentypen · Memory Management · Module & Importe
Alle Beispiele geprüft mit lyxc 1.0.17K.
<Speicherklasse> <Name> [: <Typ>] [:= <Startwert>] ;
var counter: int64 := 0; // vollständig ausgeschrieben
var counter := 0; // Typ abgeleitet
var counter: int64; // ohne Startwert — wird 0
Der Name folgt [A-Za-z_][A-Za-z0-9_]* und unterscheidet Groß- und Kleinschreibung. Das Semikolon ist Pflicht.
Reihenfolge zählt. Eine lokale Variable existiert erst ab ihrer Deklaration:
PrintLn(IntToStr(spaeter)); // sema error: undefined symbol 'spaeter'
var spaeter: int64 := 5;
| Schlüsselwort | Wert änderbar? | Wann festgelegt | Typischer Einsatz |
|---|---|---|---|
var | ja | zur Laufzeit | alles, was sich ändert: Zähler, Puffer, Zwischenergebnisse |
let | nein | einmalig bei der Deklaration | benannte Zwischenwerte, die nicht mehr wandern sollen |
co | nein | einmalig bei der Deklaration | Schwellwerte und Grenzen, die erst zur Laufzeit feststehen |
con | nein | zur Übersetzungszeit | Puffergrößen, Grenzwerte, Magic Numbers |
unit main;
import std.io;
import std.string;
con BUFFER_SIZE: int64 := 4096; // Compile-Zeit-Konstante
fn main(): int64 {
var counter: int64 := 0; // wird verändert
let name: pchar := "Lyx"; // einmalig gesetzt
co max_retries: int64 := 5; // zur Laufzeit unveränderlich
counter := counter + 1;
PrintLn(name, ": ", IntToStr(counter));
PrintLn("Puffer: ", IntToStr(BUFFER_SIZE));
PrintLn("Versuche: ", IntToStr(max_retries));
return 0;
}
Lyx: 1
Puffer: 4096
Versuche: 5
Der Unterschied ist nicht kosmetisch — der Compiler weist jeden Schreibversuch ab:
| Versuch | Meldung |
|---|---|
Zuweisung an let oder co | assignment to let/co binding not allowed 'a' |
Zuweisung an con | assignment to con constant not allowed 'A' |
con verlangt einen Ausdruck, der beim Übersetzen ausrechenbar ist. Andere con-Werte dürfen einfließen, Funktionsaufrufe nicht:
con A: int64 := 10;
con B: int64 := A * 2; // 20 — erlaubt
fn f(): int64 { return 5; }
con C: int64 := f(); // Parse error: function call not allowed in const expression
Faustregel: var nur, wo sich wirklich etwas ändert. Jede Variable, die als let, co oder con deklariert ist, kann später niemand versehentlich überschreiben — und der Compiler prüft das, statt es nur zu dokumentieren.
Ohne := ist eine Variable mit Null vorbelegt — 0 bei Zahlen, false bei bool, Adresse 0 bei pchar. Es gibt keinen „undefinierten„ Zustand wie in C:
var u: int64;
PrintLn(IntToStr(u)); // 0
Für let und co ist das eine Sackgasse: sie lassen sich nur bei der Deklaration belegen. let a: int64; übersetzt zwar, ergibt aber einen Slot, der für immer 0 bleibt — eine spätere Zuweisung wird abgewiesen. Deshalb: let und co immer mit Startwert schreiben.
Fehlt der Typ, leitet der Compiler ihn aus dem Startwert ab:
var a := 42; // int64
var b := 3.14; // f64
var c := true; // bool
var d := "Text"; // pchar
Typsuffixe am Literal (42i8, 255u8) gibt es nicht — das ist ein Parse-Fehler. Wer eine bestimmte Breite braucht, schreibt sie in die Deklaration: var b: uint8 := 255;.
Lyx kennt keine impliziten Konvertierungen; zwischen Typen wird mit as umgewandelt:
var ganzzahl: int64 := 7;
var kommazahl: f64 := ganzzahl as f64;
Zwei Lücken in der Prüfung des Startwertes. Die Regel „keine impliziten Konvertierungen“ gilt nicht überall — zwei Fälle rutschen durch und liefern stillschweigend Unsinn:
^ Deklaration ^ Verhalten ^
|var b := 3.14; var i: int64 := b;| angenommen —ienthält4614253070214989086, das Bitmuster von 3.14 |
|var f: f64 := 7;| angenommen —fenthält0.0|
|var b: f64 := 3.14; var i: int64 := b;| korrekt abgewiesen:Initialisierung: int64 erwartet, f64 gegeben|
|var i: int64 := 7; var f: f64 := i;| korrekt abgewiesen:Initialisierung: f64 erwartet, int64 gegeben|
Die Prüfung greift also bei explizit typisierten Variablen, nicht aber bei einer abgeleitetenf64und nicht bei einem ganzzahligen Literal in einerf64-Deklaration. Bis das behoben ist: Fließkommazahlen mit Punkt schreiben (var f: f64 := 7.0;) und beim Übergang zwischen Ganzzahl und Fließkomma immerassetzen. Geprüft mit 1.0.17K.
Eine Variable gilt vom Punkt ihrer Deklaration bis zum Ende des Blocks, in dem sie steht. Blöcke sind geschweifte Klammern — Funktionsrümpfe, if-Zweige, Schleifenkörper.
fn main(): int64 {
var x: int64 := 1;
if (true) {
var x: int64 := 2; // eigener Block: verdeckt das äußere x
PrintLn("innen: ", IntToStr(x));
}
PrintLn("aussen: ", IntToStr(x));
return 0;
}
innen: 2
aussen: 1
x nicht erreichbar, danach gilt es wieder unverändert.Name im selben Block bereits deklariert 'x'.fn f(x: int64) { var x: int64 := 99; … } übersetzt und liefert 99. Lesbar ist das nicht; besser einen anderen Namen wählen.
Die Laufvariable einer for-Schleife gehört zur Schleife und ist danach weg:
for j := 0 to 2 do { }
PrintLn(IntToStr(j)); // sema error: undefined symbol 'j'
Außerhalb jeder Funktion deklarierte Variablen leben für die gesamte Programmlaufzeit und sind in der ganzen Unit sichtbar. Mit pub werden sie exportiert und sind nach dem Import auch aus anderen Units erreichbar — lesend und schreibend:
// m/cfg.lyx
unit m.cfg;
pub var Schwelle: int64 := 7;
pub fn Get(): int64 { return Schwelle; }
// main.lyx
unit main;
import std.io;
import std.string;
import m.cfg;
fn main(): int64 {
Schwelle := 9; // direkter Schreibzugriff von außen
PrintLn(IntToStr(Get())); // 9
return 0;
}
Genau deshalb gilt: pub var vermeiden. Wer den Zustand über pub fn Get() und pub fn Set() kapselt, behält die Kontrolle darüber, wer wann schreibt — und kann später Prüfungen einziehen, ohne alle Aufrufer zu ändern.
Aggregate funktionieren auf Modulebene genauso wie lokal:
type S = struct { a: int64; b: int64; };
var feld: [4]int64 := [1,2,3,4];
var s: S;
var text: pchar := "global";
fn main(): int64 {
feld[2] := 99;
s.a := 7;
// feld[2]=99, s.a=7, text="global"
return 0;
}
(Bis lyxc 1.0.16x waren globale Arrays und Structs unbrauchbar — Schreibzugriffe stürzten ab oder verpufften. Seit 1.0.17C tragen sie, siehe #1256.)
Der Startwert einer globalen Variablen muss zur Übersetzungszeit feststehen. Der Wert einer anderen globalen Variablen zählt nicht dazu:
var A: int64 := 10;
var B: int64 := A * 2;
// codegen error: globale Variable 'B': Startwert ist zur Uebersetzungszeit nicht bekannt
Abhilfe: con verwenden (con A: int64 := 10; con B: int64 := A * 2; ist erlaubt) oder den Wert in einer Init()-Funktion setzen.
Globalef64rechnen seit lyxc 1.0.17K richtig — außer alscon(#1373). Eine globalevar- oderco-Variable vom Typf64liefert in jedem Ausdruck ihren Wert:var S: f64 := 85.0;vergleicht und rechnet korrekt.
Offen bleibt genaucon: Mitcon SCHWELLE: f64 := 85.0;ist86.0 >= SCHWELLEfalsch, undSCHWELLE + 0.0ergibt4635681760191971328— die Konstante kommt als rohes Bitmuster in die Rechnung. Die direkte Ausgabe überPrintF64stimmt dagegen, der Fehler fällt deshalb leicht nicht auf.
Bis zur Behebung: für Fließkomma-Schwellwertevarodercostattconnehmen, oder die Konstante zuerst in eine lokale Variable kopieren (var schwelle: f64 := SCHWELLE;). Ganzzahlige Globals sind nicht betroffen. (Issue #1373, geprüft mit 1.0.17K)
connach seiner ersten Verwendung ist seit lyxc 1.0.17K in Ordnung (#1338). Einecon-Deklaration, die erst nach der benutzenden Funktion steht, trägt jetzt ihren Wert —fn f(): int64 { return SPAETER; } con SPAETER: int64 := 42;liefert42. Bis 1.0.17I setzte der Compiler dort kommentarlos0ein.
Die Empfehlung bleibt:con-Deklarationen an den Anfang der Unit — das liest sich besser und hängt an keiner Compiler-Fassung.
| Deklaration | Liegt | Lebt | Aufräumen |
|---|---|---|---|
var / let / co in einer Funktion | Stack | bis zum Ende des Blocks | automatisch |
var auf Modulebene | Datensegment | Programmlaufzeit | entfällt |
con | im Maschinencode | – | entfällt |
array<T>, new T[n], new Klasse() | Heap | bis dispose | von Hand |
alloc(n) | Heap | bis free(ptr, size) | von Hand |
Eine Stack-Variable verschwindet mit ihrem Block. Wer ihre Adresse über das Blockende hinaus weiterreicht, greift auf fremden Speicher zu — Lyx hat keine Lebensdauer-Prüfung, die das verhindert. Details: Memory Management und Rohspeicher.
| Situation | Empfehlung |
|---|---|
| Wert ändert sich | var |
| Zwischenergebnis, das stehen bleiben soll | let |
| Grenze oder Schwelle, erst zur Laufzeit bekannt | co |
| Puffergröße, Magic Number, Protokollkonstante | con — ganz oben in der Unit |
| Fließkomma-Schwellwert | lokal halten (siehe Kasten oben) |
| Zustand, den andere Units brauchen | pub fn Get()/Set() statt pub var |
| Größe steht beim Übersetzen fest, unter ~100 KB | Stack: var puffer: [N]T; |
| Größe erst zur Laufzeit bekannt | Heap: array<T> oder alloc — mit passendem Aufräumen |
let/co ohne Startwert bleiben für immer 0 — der Compiler meldet das nicht.f64-Deklaration (var f: f64 := 7;) ergibt 0.0. Immer 7.0 schreiben.f64 in einer int64-Deklaration wird angenommen und liefert das Bitmuster.f64-Konstante (con) rechnet mit ihrem Bitmuster; globale var/co sind seit 1.0.17K in Ordnung (#1373).con nehmen oder in Init() setzen.pub var erlaubt jeder importierenden Unit den Schreibzugriff.Weiterführend: Datentypen · Syntax-Referenz · Memory Management · Module & Importe
Letzte Aktualisierung: 2026-08-13 · alle Beispiele geprüft mit lyxc 1.0.17K; con-Vorwärtsreferenz (#1338) behoben, globale f64 nur noch als con betroffen (#1373)