Déclarer un shape
Un shape est un type d'enregistrement avec des champs nommés et typés :
shape Point {
x: i32,
y: i32,
}
Les pièces :
shapeintroduit la déclaration.Pointest le nom du type.- Les accolades enferment une liste de champs, chacun en
nom: Type.
Cette déclaration ajoute un nouveau type appelé Point à la portée courante. Partout où vous pouvez utiliser un type intégré comme i32, vous pouvez aussi maintenant utiliser Point.
Construire une valeur de shape
Créez une instance avec une expression de littéral de struct :
let point = Point { x: 40, y: 2 }
Le littéral nomme le shape et affecte chaque champ. Chaque champ doit être présent — Zero ne met pas silencieusement à zéro ou à null les champs manquants. Si vous en oubliez un, le compilateur vous le dit :
{
"code": "FLD002",
"message": "missing field: y",
"line": 4
}
(Le code d'erreur exact peut varier ; le principe « pas de valeurs par défaut implicites » est la constante.)
Vous pouvez lire ou annoter le type explicitement si vous voulez qu'il soit visible :
let point: Point = Point { x: 40, y: 2 }
Lire les champs
L'accès aux champs utilise la syntaxe pointée :
let point = Point { x: 40, y: 2 }
let xVal = point.x
let yVal = point.y
point.x lit le champ x. Il n'y a pas de méthode get_x() — les champs sont de la donnée pure.
Un exemple complet
Voici l'exemple canonique point.0 du dépôt du langage — cliquez sur Run pour l'essayer :
Lisez-le du haut vers le bas :
- Déclarez un shape
Pointavec deux champsi32. - Définissez une fonction
sumqui prend unPointet retourne la somme de ses champs. - Dans
main, construisez unPoint, appelezsum, et comparez le résultat.
Trois étapes, trois idées liées aux shapes (déclarer, construire, accéder), et un effet — le check world.out.write(...) à la fin. Remarquez que sum ne touche pas World. C'est une fonction pure sur des données, et la signature le rend évident.
Shapes avec des champs imbriqués
Un shape peut contenir des valeurs de n'importe quel type, y compris d'autres shapes :
shape Range {
start: i32,
end: i32,
}
shape Segment {
label: String,
range: Range,
}
let seg = Segment {
label: "warmup",
range: Range { start: 0, end: 10 },
}
L'accès aux champs s'enchaîne comme prévu :
let len = seg.range.end - seg.range.start
Shapes génériques
Quand les champs d'un shape doivent être polymorphes en type, déclarez des paramètres de type entre chevrons :
shape Pair<T, U> {
left: T,
right: U,
}
Les instances fixent les paramètres à des types concrets :
let intBytePair: Pair<i32, u8> = Pair { left: 40, right: 2_u8 }
let words: Pair<String, String> = Pair { left: "hello", right: "world" }
Un alias de type peut raccourcir une paramétrisation courante :
type BytePair = Pair<u8, u8>
let bytes: BytePair = Pair { left: 1_u8, right: 2_u8 }
Generics couvre les paramètres de type plus en profondeur — y compris sur les fonctions, pas seulement sur les shapes.
Ce que les shapes ne sont pas
Quelques choses qu'on pourrait attendre d'une « struct » dans un autre langage que les shapes n'incluent délibérément pas :
- Pas de méthodes. Une déclaration de shape, c'est juste de la donnée. Le comportement vit dans des fonctions libres qui prennent le shape en paramètre. Cela reflète la même séparation entre données et effets que vous voyez dans les fonctions.
- Pas d'héritage. Les shapes n'étendent pas d'autres shapes. Si vous voulez une structure partagée, factorisez-la dans un champ commun ou construisez un type somme avec choice.
- Pas de constructeurs ou destructeurs implicites. La construction, c'est l'expression de littéral de struct. Le nettoyage est explicite — quand la bibliothèque standard expose des ressources qui ont besoin d'être libérées, ça passe par des APIs façon capacité plutôt que par un RAII caché.
- Pas de champs privés. Tous les champs d'un shape sont accessibles au code qui peut voir le type du shape. La visibilité est au niveau du type, pas du champ.
Le motif est : les shapes sont des types d'enregistrement simples et prévisibles, et vous construisez tout le reste à partir d'eux.
Quand utiliser un shape vs. un choice
Petit guide :
- Utilisez un shape quand une valeur a tous ces champs ensemble. Un
Pointa toujours à la fois unxet uny. - Utilisez un choice quand une valeur est l'une parmi plusieurs alternatives. Un
Resultest soit unoksoit unerr. - Utilisez un enum quand les alternatives ne portent aucune donnée supplémentaire — ce sont juste des étiquettes. Jours de la semaine, états simples.
Ces trois briques — shape (et), choice (ou), enum (ou sans charge utile) — couvrent presque tous les besoins de modélisation de données.
La suite : les génériques
Vous avez vu Pair<T, U> apparaître au passage. Le prochain document, generics, explique comment les paramètres de type fonctionnent sur les shapes et les fonctions, y compris les motifs qui apparaissent partout dans la bibliothèque standard de Zero.
Questions fréquentes
Qu'est-ce qu'un shape en Zero ?
Un shape est le type produit à la struct de Zero — un enregistrement nommé avec des champs typés. Vous le déclarez avec shape Name { field1: T1, field2: T2 }, construisez des valeurs avec Name { field1: v1, field2: v2 }, et lisez les champs avec la syntaxe pointée (value.field1). Les shapes sont la brique de base pour modéliser des données structurées.
Comment créer une valeur de shape ?
Utilisez une expression de littéral de struct qui nomme le shape et affecte chaque champ : let point = Point { x: 40, y: 2 }. Chaque champ doit être renseigné — Zero ne met pas silencieusement des valeurs par défaut sur les champs manquants. L'ordre des champs dans le littéral n'a pas à coller à celui de la déclaration.
En quoi un shape diffère-t-il d'une classe ?
Un shape est de la donnée pure — il a des champs, mais pas de méthodes, pas d'héritage, pas de constructeurs implicites. Les fonctions qui opèrent sur un shape le prennent explicitement en paramètre. Cette séparation garde le langage petit et rend prévisible le coût de construire ou copier un shape, sans vtables ou destructeurs cachés.
Les shapes peuvent-ils être génériques en Zero ?
Oui. Déclarez des paramètres de type entre chevrons : shape Pair<T, U> { left: T, right: U }. Les instances fixent ces paramètres : Pair<i32, u8>. Les shapes génériques apparaissent partout dans la bibliothèque standard — Maybe<T>, Span<T>, etc., sont tous des shapes ou types somme génériques bâtis sur la même idée.
Les shapes sont-ils copiés ou référencés quand on les passe à des fonctions ?
Le modèle mental par défaut pour les shapes est le passage par valeur — l'appelé voit sa propre copie logique des données, pas une référence dans la liaison de l'appelant. Le modèle mémoire exact en Zero pré-1.0 évolue encore (vous verrez ref et mutref dans les exemples de la bibliothèque standard pour des types de référence explicites). Pour la plupart du code applicatif, considérez les paramètres de shape comme des entrées par valeur.