Menu

Shape in Zero: definire record simili alle struct

Le shape sono i tipi prodotto di Zero, simili alle struct. Ecco come dichiararle, costruire valori, leggere i campi e passarle alle funzioni, con esempi tratti dai campioni ufficiali.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Dichiarare una shape

Una shape è un tipo record con campi dotati di nome e tipo:

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

Le parti:

  • shape introduce la dichiarazione.
  • Point è il nome del tipo.
  • Le parentesi graffe racchiudono un elenco di campi, ciascuno nella forma name: Type.

Questa dichiarazione aggiunge allo scope corrente un nuovo tipo chiamato Point. Ovunque puoi usare un tipo integrato come i32, ora puoi usare anche Point.

Costruire un valore shape

Crea un'istanza con un'espressione letterale di struttura:

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

Il letterale indica la shape e assegna ogni campo. Ogni campo deve essere presente: Zero non imposta in silenzio a zero o a null i campi mancanti. Se ne dimentichi uno, il compilatore te lo dice:

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

(Il codice di errore esatto può variare; il principio "nessun valore predefinito implicito" è la costante.)

Se vuoi che il tipo sia visibile, puoi annotarlo esplicitamente:

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

Leggere i campi

L'accesso ai campi usa la notazione a punto:

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

point.x legge il campo x. Non esiste un metodo get_x(): i campi sono semplici dati.

Un esempio completo

Questo è il classico esempio point.0 del repository del linguaggio: clicca su Esegui per provarlo:

Seguilo dall'alto:

  1. Dichiara una shape Point con due campi i32.
  2. Definisci una funzione sum che riceve un Point e restituisce la somma dei suoi campi.
  3. In main, costruisci un Point, chiama sum e confronta il risultato.

Tre passaggi, tre idee legate alle shape (dichiarare, costruire, accedere) e un solo effetto: il check world.out.write(...) alla fine. Nota che sum non tocca World. È una funzione pura sui dati, e la firma lo rende evidente.

Shape con campi annidati

Una shape può contenere valori di qualsiasi tipo, comprese altre shape:

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

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

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

L'accesso ai campi si concatena come ti aspetti:

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

Shape generiche

Quando i campi di una shape devono essere polimorfici rispetto al tipo, dichiara i parametri di tipo tra parentesi angolari:

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

Le istanze fissano i parametri su tipi concreti:

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

Un alias di tipo può abbreviare una parametrizzazione frequente:

type BytePair = Pair<u8, u8>

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

Generics approfondisce i parametri di tipo, anche sulle funzioni e non solo sulle shape.

Cosa non sono le shape

Alcune cose che potresti aspettarti da una "struct" in un altro linguaggio e che le shape di proposito non includono:

  • Niente metodi. La dichiarazione di una shape contiene solo dati. Il comportamento vive in funzioni libere che ricevono la shape come parametro. Rispecchia la stessa separazione tra dati ed effetti che vedi nelle funzioni.
  • Niente ereditarietà. Le shape non estendono altre shape. Se vuoi una struttura condivisa, raccoglila in un campo comune o costruisci un tipo somma con choice.
  • Niente costruttori o distruttori impliciti. La costruzione è l'espressione letterale di struttura. La pulizia è esplicita: quando la libreria standard espone risorse da rilasciare, lo fa tramite API in stile capability invece che con un RAII nascosto.
  • Niente campi privati. Tutti i campi di una shape sono accessibili al codice che vede il tipo della shape. La visibilità si decide a livello di tipo, non di campo.

Lo schema è questo: le shape sono tipi record semplici e prevedibili, e tutto il resto lo costruisci a partire da loro.

Quando usare una shape o una choice

Una guida rapida:

  • Usa una shape quando un valore ha tutti questi campi insieme. Un Point ha sempre sia una x sia una y.
  • Usa una choice quando un valore è una tra diverse alternative. Un Result è o un ok o un err.
  • Usa un enum quando le alternative non portano dati aggiuntivi: sono solo etichette. Giorni della settimana, stati semplici.

Questi tre mattoni, cioè shape (e), choice (o) ed enum (o senza payload), coprono quasi ogni esigenza di modellazione dei dati.

Prossimo passo: generics

Hai già visto comparire Pair<T, U> di sfuggita. La prossima pagina, generics, spiega come funzionano i parametri di tipo sia sulle shape sia sulle funzioni, compresi gli schemi che ricorrono in tutta la libreria standard di Zero.

Domande frequenti

Cos'è una shape in Zero?

Una shape è il tipo prodotto di Zero, simile a una struct: un record con nome e campi tipizzati. La dichiari con shape Name { field1: T1, field2: T2 }, costruisci valori con Name { field1: v1, field2: v2 } e leggi i campi con la notazione a punto (value.field1). Le shape sono il mattone base per modellare dati strutturati.

Come si crea un valore shape?

Usa un'espressione letterale di struttura che indica la shape e assegna ogni campo: let point = Point { x: 40, y: 2 }. Ogni campo va compilato: Zero non assegna in silenzio valori predefiniti ai campi mancanti. L'ordine dei campi nel letterale non deve per forza corrispondere a quello della dichiarazione.

In cosa una shape è diversa da una classe?

Una shape è solo dati: ha campi, ma niente metodi, niente ereditarietà, niente costruttori impliciti. Le funzioni che operano su una shape la ricevono esplicitamente come parametro. Questa separazione mantiene piccolo il linguaggio e rende prevedibile il costo di costruire o copiare una shape, senza vtable o distruttori nascosti.

Le shape possono essere generiche in Zero?

Sì. Dichiara i parametri di tipo tra parentesi angolari: shape Pair<T, U> { left: T, right: U }. Le istanze fissano quei parametri: Pair<i32, u8>. Le shape generiche compaiono in tutta la libreria standard: Maybe<T>, Span<T> e così via sono tutte shape generiche o tipi somma costruiti sulla stessa idea.

Le shape passate alle funzioni vengono copiate o passate per riferimento?

Il passaggio per valore è il modello mentale predefinito per le shape: la funzione chiamata vede la propria copia logica dei dati, non un riferimento al binding del chiamante. Il modello di memoria esatto dello Zero pre-1.0 è ancora in evoluzione (negli esempi della libreria standard vedrai ref e mutref per i tipi riferimento espliciti). Per la maggior parte del codice applicativo, tratta i parametri shape come input passati per valore.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA