Declarar un shape
Un shape es un tipo registro con campos nombrados y tipados:
shape Point {
x: i32,
y: i32,
}
Las piezas:
shapeintroduce la declaración.Pointes el nombre del tipo.- Las llaves encierran una lista de campos, cada uno
name: Type.
Esa declaración añade un nuevo tipo llamado Point al ámbito actual. Donde puedas usar un tipo integrado como i32, ahora también puedes usar Point.
Construir un valor de shape
Crea una instancia con una expresión de literal de struct:
let point = Point { x: 40, y: 2 }
El literal nombra el shape y asigna cada campo. Todos los campos tienen que estar presentes — Zero no rellena silenciosamente con cero o nulo los que falten. Si olvidas uno, el compilador te lo dice:
{
"code": "FLD002",
"message": "missing field: y",
"line": 4
}
(El código de error exacto puede variar; el principio de "sin valores por defecto implícitos" es la constante.)
Puedes anotar el tipo explícitamente si quieres que quede visible:
let point: Point = Point { x: 40, y: 2 }
Leer campos
El acceso a campos usa la sintaxis de punto:
let point = Point { x: 40, y: 2 }
let xVal = point.x
let yVal = point.y
point.x lee el campo x. No hay un método get_x(): los campos son datos puros.
Un ejemplo completo
Este es el ejemplo canónico point.0 del repositorio del lenguaje — pulsa Ejecutar para probarlo:
Léelo de arriba abajo:
- Declara un shape
Pointcon dos camposi32. - Define una función
sumque toma unPointy devuelve la suma de sus campos. - En
main, construye unPoint, llama asumy compara el resultado.
Tres pasos, tres ideas relacionadas con shapes (declarar, construir, acceder) y un efecto: el check world.out.write(...) del final. Fíjate en que sum no toca World. Es una función pura sobre datos, y la firma lo deja claro.
Shapes con campos anidados
Un shape puede contener valores de cualquier tipo, incluyendo otros shapes:
shape Range {
start: i32,
end: i32,
}
shape Segment {
label: String,
range: Range,
}
let seg = Segment {
label: "warmup",
range: Range { start: 0, end: 10 },
}
El acceso a campos se encadena como esperarías:
let len = seg.range.end - seg.range.start
Shapes genéricos
Cuando los campos de un shape deben ser polimórficos en el tipo, declara parámetros de tipo entre paréntesis angulares:
shape Pair<T, U> {
left: T,
right: U,
}
Las instancias fijan los parámetros a tipos concretos:
let intBytePair: Pair<i32, u8> = Pair { left: 40, right: 2_u8 }
let words: Pair<String, String> = Pair { left: "hello", right: "world" }
Un alias de tipo puede acortar una parametrización común:
type BytePair = Pair<u8, u8>
let bytes: BytePair = Pair { left: 1_u8, right: 2_u8 }
Genéricos cubre los parámetros de tipo en más profundidad, incluyendo los usados en funciones, no solo en shapes.
Lo que los shapes no son
Algunas cosas que podrías esperar de un "struct" de otro lenguaje y que los shapes deliberadamente no incluyen:
- Sin métodos. Una declaración de shape son solo datos. El comportamiento vive en funciones libres que toman el shape como parámetro. Esto refleja la misma separación entre datos y efectos que ves en funciones.
- Sin herencia. Los shapes no extienden otros shapes. Si quieres estructura compartida, factorízala en un campo común o construye un tipo suma con choice.
- Sin constructores o destructores implícitos. La construcción es la expresión de literal de struct. La limpieza es explícita: cuando la biblioteca estándar expone recursos que necesitan liberación, se hace mediante APIs estilo capacidad, no con RAII oculto.
- Sin campos privados. Todos los campos de un shape son accesibles para el código que pueda ver el tipo del shape. La visibilidad está a nivel de tipo, no de campo.
El patrón es: los shapes son tipos registro simples y predecibles, y a partir de ellos construyes todo lo demás.
Cuándo usar un shape vs. un choice
Guía rápida:
- Usa un shape cuando un valor tiene todos esos campos juntos. Un
Pointsiempre tiene unaxy unay. - Usa un choice cuando un valor es uno de varios alternativas. Un
Resultes o bien unoko bien unerr. - Usa un enum cuando las alternativas no llevan datos extra — son solo etiquetas. Días de la semana, estados simples.
Esas tres piezas — shape (y), choice (o), enum (o sin payload) — cubren casi cualquier necesidad de modelado de datos.
Lo siguiente: genéricos
Has visto aparecer Pair<T, U> de pasada. La próxima página, genéricos, explica cómo funcionan los parámetros de tipo en shapes y funciones, incluyendo los patrones que aparecen por toda la biblioteca estándar de Zero.
Preguntas frecuentes
¿Qué es un shape en Zero?
Un shape es el tipo producto al estilo struct de Zero: un registro con nombre y campos tipados. Lo declaras con shape Name { field1: T1, field2: T2 }, construyes valores con Name { field1: v1, field2: v2 } y lees campos con sintaxis de punto (value.field1). Los shapes son la pieza para modelar datos estructurados.
¿Cómo se crea un valor de shape?
Usa una expresión de literal de struct que nombre el shape y asigne cada campo: let point = Point { x: 40, y: 2 }. Hay que rellenar todos los campos — Zero no rellena por defecto los que faltan en silencio. El orden de los campos en el literal no tiene que coincidir con el de la declaración.
¿En qué se diferencia un shape de una clase?
Un shape son datos puros — tiene campos, pero no métodos, ni herencia, ni constructores implícitos. Las funciones que operan sobre un shape lo reciben como parámetro explícito. Esa separación mantiene el lenguaje pequeño y hace predecible el coste de construir o copiar un shape, sin vtables ni destructores ocultos.
¿Pueden ser genéricos los shapes en Zero?
Sí. Declara parámetros de tipo entre paréntesis angulares: shape Pair<T, U> { left: T, right: U }. Las instancias fijan esos parámetros: Pair<i32, u8>. Los shapes genéricos aparecen por toda la biblioteca estándar — Maybe<T>, Span<T>, etcétera, son shapes genéricos o tipos suma construidos sobre la misma idea.
¿Los shapes se copian o se pasan por referencia a las funciones?
El modelo mental por defecto para los shapes es paso por valor: la función receptora ve su propia copia lógica de los datos, no una referencia a la ligadura del llamador. El modelo de memoria exacto en Zero pre-1.0 sigue evolucionando (verás ref y mutref en los ejemplos de la biblioteca estándar para tipos de referencia explícitos). Para la mayoría del código de aplicación, trata los parámetros shape como entradas por valor.