Menu

Zero Shapes: Struct-artige Records definieren

Shapes sind Zeros struct-artige Produkttypen. So deklarierst du sie, baust Werte, liest Felder und reichst sie durch Funktionen – mit Beispielen aus den offiziellen Samples.

Diese Seite enthält ausführbare Editoren — bearbeiten, ausführen und Ausgabe sofort sehen.

Eine Shape deklarieren

Eine shape ist ein Record-Typ mit benannten, typisierten Feldern:

shape Point {
    x: i32,
    y: i32,
}

Bestandteile:

  • shape leitet die Deklaration ein.
  • Point ist der Name des Typs.
  • Die geschweiften Klammern umschließen eine Liste von Feldern, jeweils name: Typ.

Die Deklaration fügt einen neuen Typ namens Point zum aktuellen Scope hinzu. Überall, wo du einen eingebauten Typ wie i32 verwenden kannst, darfst du jetzt auch Point verwenden.

Einen Shape-Wert konstruieren

Erzeuge eine Instanz mit einem Struct-Literal-Ausdruck:

let point = Point { x: 40, y: 2 }

Das Literal benennt die Shape und füllt jedes Feld. Jedes Feld muss vorhanden sein – Zero füllt fehlende Felder nicht stillschweigend mit Null oder Null. Vergisst du eines, sagt der Compiler dir Bescheid:

{
    "code": "FLD002",
    "message": "missing field: y",
    "line": 4
}

(Der genaue Fehlercode kann variieren; der Grundsatz „keine impliziten Defaults" ist die Konstante.)

Du kannst den Typ explizit annotieren, wenn du ihn sichtbar haben willst:

let point: Point = Point { x: 40, y: 2 }

Felder lesen

Feldzugriff nutzt Punkt-Notation:

let point = Point { x: 40, y: 2 }
let xVal  = point.x
let yVal  = point.y

point.x liest das x-Feld. Es gibt keine get_x()-Methode – Felder sind reine Daten.

Ein komplettes durchgespieltes Beispiel

Das ist das kanonische point.0-Beispiel aus dem Repository der Sprache – klick Run, um es auszuprobieren:

Von oben durchgehen:

  1. Eine Point-Shape mit zwei i32-Feldern deklarieren.
  2. Eine sum-Funktion definieren, die ein Point nimmt und die Summe seiner Felder zurückgibt.
  3. In main ein Point konstruieren, sum aufrufen und das Ergebnis vergleichen.

Drei Schritte, drei Shape-bezogene Ideen (deklarieren, konstruieren, zugreifen) und ein Effekt – das check world.out.write(...) am Ende. Beachte, dass sum World nicht anfasst. Es ist eine reine Funktion über Daten, und die Signatur macht das offensichtlich.

Shapes mit verschachtelten Feldern

Eine Shape kann Werte beliebigen Typs halten, auch andere Shapes:

shape Range {
    start: i32,
    end: i32,
}

shape Segment {
    label: String,
    range: Range,
}

let seg = Segment {
    label: "warmup",
    range: Range { start: 0, end: 10 },
}

Feldzugriff kettet sich so, wie du es erwartest:

let len = seg.range.end - seg.range.start

Generische Shapes

Wenn die Felder einer Shape typ-polymorph sein sollen, deklariere Typparameter in spitzen Klammern:

shape Pair<T, U> {
    left: T,
    right: U,
}

Instanzen pinnen die Parameter auf konkrete Typen:

let intBytePair: Pair<i32, u8> = Pair { left: 40, right: 2_u8 }
let words: Pair<String, String> = Pair { left: "hello", right: "world" }

Ein Typ-Alias kann eine häufige Parametrisierung abkürzen:

type BytePair = Pair<u8, u8>

let bytes: BytePair = Pair { left: 1_u8, right: 2_u8 }

Generics behandelt Typparameter ausführlicher – auch bei Funktionen, nicht nur bei Shapes.

Was Shapes nicht sind

Ein paar Dinge, die du von einem „struct" in anderen Sprachen erwarten könntest, die Shapes bewusst nicht enthalten:

  • Keine Methoden. Eine Shape-Deklaration sind nur Daten. Verhalten lebt in freien Funktionen, die die Shape als Parameter nehmen. Das spiegelt dieselbe Trennung von Daten und Effekten wie bei Funktionen.
  • Keine Vererbung. Shapes erweitern keine anderen Shapes. Willst du gemeinsame Struktur, faktorisiere sie in ein gemeinsames Feld oder bau einen Summentyp mit choice.
  • Keine impliziten Konstruktoren oder Destruktoren. Konstruktion ist der Struct-Literal-Ausdruck. Cleanup ist explizit – wenn die Standardbibliothek Ressourcen herausgibt, die freigegeben werden müssen, läuft das über Capability-artige APIs statt über verstecktes RAII.
  • Keine privaten Felder. Alle Felder einer Shape sind für Code zugänglich, der den Shape-Typ sehen kann. Sichtbarkeit ist auf Typ-Ebene, nicht auf Feld-Ebene.

Das Muster ist: Shapes sind einfache, vorhersehbare Record-Typen, und du baust alles andere aus ihnen.

Wann Shape, wann Choice?

Kurze Orientierung:

  • Nimm eine shape, wenn ein Wert alle diese Felder zusammen hat. Ein Point hat immer sowohl x als auch y.
  • Nimm eine choice, wenn ein Wert einer von mehreren Alternativen ist. Ein Result ist entweder ein ok oder ein err.
  • Nimm ein enum, wenn die Alternativen keine extra Daten tragen – sie sind nur Labels. Wochentage, einfache Zustände.

Diese drei Bausteine – shape (und), choice (oder), enum (oder ohne Payload) – decken fast jeden Modellierungsbedarf ab.

Als Nächstes: Generics

Du hast Pair<T, U> schon vorbeihuschen sehen. Das nächste Dokument, Generics, erklärt, wie Typparameter auf Shapes und Funktionen funktionieren, einschließlich der Muster, die überall in Zeros Standardbibliothek auftauchen.

Häufig gestellte Fragen

Was ist eine Shape in Zero?

Eine shape ist Zeros struct-artiger Produkttyp – ein benannter Record mit typisierten Feldern. Du deklarierst sie mit shape Name { field1: T1, field2: T2 }, baust Werte mit Name { field1: v1, field2: v2 } und liest Felder per Punkt-Notation (value.field1). Shapes sind der Baustein, um strukturierte Daten zu modellieren.

Wie erzeuge ich einen Shape-Wert?

Mit einem Struct-Literal-Ausdruck, der die Shape benennt und jedes Feld füllt: let point = Point { x: 40, y: 2 }. Jedes Feld muss befüllt sein – Zero befüllt fehlende Felder nicht stillschweigend mit einem Default. Die Reihenfolge der Felder im Literal muss nicht der Deklaration entsprechen.

Was unterscheidet eine shape von einer Klasse?

Eine Shape ist reine Daten – sie hat Felder, aber keine Methoden, keine Vererbung, keine impliziten Konstruktoren. Funktionen, die auf einer Shape arbeiten, nehmen sie explizit als Parameter. Diese Trennung hält die Sprache klein und macht die Kosten fürs Bauen oder Kopieren einer Shape vorhersehbar – keine versteckten vtables oder Destruktoren.

Können Shapes in Zero generisch sein?

Ja. Deklariere Typparameter in spitzen Klammern: shape Pair<T, U> { left: T, right: U }. Instanzen pinnen diese Parameter fest: Pair<i32, u8>. Generische Shapes tauchen überall in der Standardbibliothek auf – Maybe<T>, Span<T> und so weiter sind alles generische Shapes oder Summentypen, die auf derselben Idee aufbauen.

Werden Shapes beim Übergeben an Funktionen kopiert oder referenziert?

Das mentale Modell für Shapes ist pass-by-value – die aufgerufene Funktion sieht ihre eigene logische Kopie der Daten, keine Referenz auf das Binding des Aufrufers. Das exakte Speichermodell in pre-1.0 Zero entwickelt sich noch (du wirst ref und mutref in den Beispielen der Standardbibliothek für explizite Referenztypen sehen). Für den Großteil von Anwendungscode behandle Shape-Parameter als wertbasierte Eingaben.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S