Menu

Shape w Zero: definiowanie rekordów podobnych do struktur

Shape to typy produktowe Zero podobne do struktur. Zobacz, jak je deklarować, tworzyć wartości, czytać pola i przekazywać je przez funkcje, na przykładach z oficjalnych próbek.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Deklarowanie shape

shape to typ rekordowy z nazwanymi polami o określonych typach:

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

Elementy:

  • shape rozpoczyna deklarację.
  • Point to nazwa typu.
  • Nawiasy klamrowe obejmują listę pól, każde w formie name: Type.

Ta deklaracja dodaje do bieżącego zakresu nowy typ o nazwie Point. Wszędzie tam, gdzie możesz użyć typu wbudowanego, takiego jak i32, możesz teraz użyć też Point.

Tworzenie wartości shape

Instancję tworzy się wyrażeniem literału struktury:

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

Literał podaje nazwę shape i przypisuje każde pole. Każde pole musi być obecne: Zero nie ustawia po cichu brakujących pól na zero ani null. Jeśli o którymś zapomnisz, kompilator ci o tym powie:

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

(Dokładny kod błędu może się różnić; stała jest zasada "żadnych niejawnych wartości domyślnych".)

Możesz też jawnie podać typ, jeśli chcesz, żeby był widoczny:

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

Odczyt pól

Dostęp do pól odbywa się przez kropkę:

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

point.x odczytuje pole x. Nie ma metody get_x(): pola to zwykłe dane.

Pełny przykład

To kanoniczny przykład point.0 z repozytorium języka. Kliknij Run, aby go wypróbować:

Przejdźmy przez niego od góry:

  1. Zadeklaruj shape Point z dwoma polami i32.
  2. Zdefiniuj funkcję sum, która przyjmuje Point i zwraca sumę jego pól.
  3. W main utwórz Point, wywołaj sum i porównaj wynik.

Trzy kroki, trzy idee związane z shape (deklaracja, tworzenie, dostęp) i jeden efekt: check world.out.write(...) na końcu. Zwróć uwagę, że sum nie dotyka World. To czysta funkcja na danych, a sygnatura czyni to oczywistym.

Shape z zagnieżdżonymi polami

Shape może przechowywać wartości dowolnego typu, także inne shape:

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

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

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

Dostęp do pól łączy się w łańcuch, tak jak można się spodziewać:

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

Generyczne shape

Gdy pola shape mają być polimorficzne względem typu, zadeklaruj parametry typu w nawiasach ostrych:

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

Instancje ustalają parametry jako konkretne typy:

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

Alias typu może skrócić często używaną parametryzację:

type BytePair = Pair<u8, u8>

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

Artykuł Generyki omawia parametry typu dokładniej, także w funkcjach, a nie tylko w shape.

Czym shape nie są

Kilka rzeczy, których można oczekiwać od "struktury" w innym języku, a których shape celowo nie mają:

  • Brak metod. Deklaracja shape to same dane. Zachowanie żyje w wolnych funkcjach, które przyjmują shape jako parametr. Odzwierciedla to ten sam podział na dane i efekty, który widać w funkcjach.
  • Brak dziedziczenia. Shape nie rozszerzają innych shape. Jeśli potrzebujesz wspólnej struktury, wydziel ją do wspólnego pola albo zbuduj typ sumowy przez choice.
  • Brak niejawnych konstruktorów i destruktorów. Tworzenie to wyrażenie literału struktury. Sprzątanie jest jawne: gdy biblioteka standardowa udostępnia zasoby wymagające zwolnienia, odbywa się to przez API w stylu capabilities, a nie ukryte RAII.
  • Brak pól prywatnych. Wszystkie pola shape są dostępne dla kodu, który widzi typ shape. Widoczność działa na poziomie typu, a nie pola.

Wzorzec jest taki: shape to proste, przewidywalne typy rekordowe, a wszystko inne buduje się z nich.

Kiedy shape, a kiedy choice

Krótki przewodnik:

  • Użyj shape, gdy wartość ma wszystkie te pola naraz. Point zawsze ma zarówno x, jak i y.
  • Użyj choice, gdy wartość jest jedną z kilku możliwości. Result to albo ok, albo err.
  • Użyj enum, gdy możliwości nie niosą dodatkowych danych, tylko są etykietami. Dni tygodnia, proste stany.

Te trzy elementy, czyli shape (i), choice (lub) oraz enum (lub bez danych), pokrywają niemal każdą potrzebę modelowania danych.

Dalej: generyki

Pair<T, U> pojawiało się już mimochodem. Następny artykuł, generyki, wyjaśnia, jak działają parametry typu zarówno w shape, jak i w funkcjach, łącznie ze wzorcami występującymi w całej bibliotece standardowej Zero.

Najczęściej zadawane pytania

Czym jest shape w Zero?

shape to typ produktowy Zero podobny do struktury: nazwany rekord z polami o określonych typach. Deklarujesz go przez shape Name { field1: T1, field2: T2 }, tworzysz wartości przez Name { field1: v1, field2: v2 }, a pola czytasz składnią z kropką (value.field1). Shape to podstawowy element modelowania danych strukturalnych.

Jak utworzyć wartość shape?

Użyj wyrażenia literału struktury, które podaje nazwę shape i przypisuje każde pole: let point = Point { x: 40, y: 2 }. Każde pole musi być wypełnione: Zero nie uzupełnia po cichu brakujących pól wartościami domyślnymi. Kolejność pól w literale nie musi odpowiadać deklaracji.

Czym shape różni się od klasy?

Shape to zwykłe dane: ma pola, ale nie ma metod, dziedziczenia ani niejawnych konstruktorów. Funkcje działające na shape przyjmują go jawnie jako parametr. Ten podział utrzymuje język małym, a koszt budowania lub kopiowania shape przewidywalnym, bez ukrytych vtable czy destruktorów.

Czy shape w Zero mogą być generyczne?

Tak. Zadeklaruj parametry typu w nawiasach ostrych: shape Pair<T, U> { left: T, right: U }. Instancje ustalają te parametry: Pair<i32, u8>. Generyczne shape występują w całej bibliotece standardowej: Maybe<T>, Span<T> i podobne to generyczne shape lub typy sumowe oparte na tej samej idei.

Czy shape są kopiowane czy przekazywane przez referencję?

Domyślny model myślowy dla shape to przekazywanie przez wartość: wywoływana funkcja widzi własną logiczną kopię danych, a nie referencję do wiązania wywołującego. Dokładny model pamięci w Zero przed wersją 1.0 wciąż się kształtuje (w przykładach biblioteki standardowej zobaczysz ref i mutref jako jawne typy referencyjne). W większości kodu aplikacji traktuj parametry shape jako wejścia przekazywane przez wartość.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ