Guía 10 de 53
Records, maps y actualizaciones
Records, maps, proyecciones y caminos de campos reutilizables.
En esta página
Records, maps, proyecciones y caminos de campos reutilizables.
Actualizaciones inmutables anidadas¶
La actualización with evalúa la base una vez y copia la raíz y los records modificados del camino. Los hijos intactos siguen compartidos. El resultado conserva su tipo nominal; se rechazan caminos conflictivos y copias de recursos lineales.
Practica en By Example · Programa fuente
// Item 4: copy only the record paths being changed, keeping the same type.
struct Address { city: str, postal_code: str }
struct User { name: str, address: Address, roles: [str] }
fn main() {
let original = User {
name: "João",
address: Address { city: "São Paulo", postal_code: "01000-000" },
roles: ["reader"],
}
let changed = original with { name: "Ana", address.city: "Recife" }
print(original.name)
// expected: João
print(changed.name)
// expected: Ana
print(original.address.city)
// expected: São Paulo
print(changed.address.city)
// expected: Recife
print(changed.address.postal_code)
// expected: 01000-000
// The untouched roles reference is shared, while address was copied.
changed.roles.push("reviewer")
print(original.roles.len())
// expected: 2
let next = changed with { address.postal_code: "50000-000" } with { name: "Beatriz" }
print(next.name)
// expected: Beatriz
}Tipos de map explícitos¶
Escribe los tipos de map como #{K: V}. Las llaves con campos nombrados siempre describen un record, incluso campos llamados int o String. Las llaves vacías representan un record vacío. Map<K, V> sigue disponible.
Practica en By Example · Programa fuente
// Map types and map values use the same explicit delimiter.
let counts: #{str: int} = #{ ok: 2, failed: 1 }
print(counts["ok"])
// expected: 2
// Field labels never turn a record annotation into a map.
let row: { str: int, User: str, type: bool } = #{ str: 7, User: "Ada", type: true }
print(row.str)
// expected: 7
print(row.User)
// expected: Ada
print(row.type)
// expected: true
type Lookup = #{str: [#{int: str}]}
let nested: Lookup = #{ first: [#{ [1]: "one" }] }
print(nested["first"][0][1])
// expected: oneConstrucción contextual¶
Cuando se espera un tipo struct exacto, .{ campos } construye ese struct sin repetir su nombre. Siguen aplicándose los valores por defecto, la privacidad y los campos obligatorios. El compilador nunca adivina una identidad nominal por los nombres de campos.
Practica en By Example · Programa fuente
// A known nominal context supplies the construction name.
struct Point { x: int = 0, y: int = 0 }
struct Segment { start: Point, end: Point }
fn origin() -> Point { .{} }
fn magnitude(point: Point) -> int { point.x + point.y }
fn main() {
let x = 3
let point: Point = .{ x, y: 4 }
print(magnitude(.{ x: 5 })) // expected: 5
print(point.y) // expected: 4
let line: Segment = .{ start: .{ x: 1 }, end: .{ x: 7, y: 2 } }
print(line.end.x) // expected: 7
let shifted: Point = .{ x: point.x + 10, ..point }
print(shifted.x) // expected: 13
}Caminos de campos tipados¶
field User::address.city crea un FieldPath<User, str> sin leer una instancia. get lee el campo; set devuelve una actualización inmutable y comparte hijos intactos. Conserva privacidad y tipos exactos; se rechazan miembros calculados y almacenamiento lineal.
Practica en By Example · Programa fuente
// Item 20: a typed field path can be reused and its getter is a callback.
struct Address { city: str, postal_code: str }
struct User { name: str, address: Address, roles: [str] }
fn city_path() -> FieldPath<User, str> = field User::address.city
fn main() {
let city = city_path()
let original = User {
name: "Ana",
address: Address { city: "São Paulo", postal_code: "01000-000" },
roles: ["reader"],
}
let moved = city.set(original, "Recife")
print(city.get(original))
// expected: São Paulo
print(city.get(moved))
// expected: Recife
print(moved.address.postal_code)
// expected: 01000-000
let cities = [original, moved].map(city.get)
print(cities[0])
// expected: São Paulo
print(cities[1])
// expected: Recife
// The untouched collection is still shared by the immutable update.
moved.roles.push("reviewer")
print(original.roles.len())
// expected: 2
}Records anónimos independientes¶
Sin un tipo nominal esperado, .{ campos } infiere un record anónimo con tipos exactos y acceso normal a campos. Un struct esperado selecciona la construcción nominal contextual. Un tipo esperado inválido genera un diagnóstico y no se sustituye silenciosamente por un tipo estructural.
Practica en By Example · Programa fuente
// Anonymous records keep the exact type of each field.
// A nominal annotation still selects that type's ordinary constructor.
fn main() {
let count = 3
let summary = .{ count, total: 12 }
let next = summary with { total: 15 }
print(summary.count, summary.total)
print(next.total)
// expected: 3 12
// expected: 15
}
main()Actualizaciones inmutables calculadas¶
Una actualización with acepta operadores compuestos como value += 1. Lee el campo anterior una vez y calcula el reemplazo sin modificar el original. Siguen aplicándose restricciones de visibilidad, tipos, caminos conflictivos y copias lineales.
Practica en By Example · Programa fuente
struct Counter { value: int, limit: int? }
let current = Counter { value: 2, limit: nil }
let next = current with { value += 1, limit ??= 10 }
print(current.value) // 2
print(next.value) // 3
print(next.limit) // 10Comprensiones de maps¶
Construye maps con entradas for e if dentro de #{ }. Las claves calculadas usan [expresión]: valor. Iterables, claves y valores se evalúan una vez en orden; las claves repetidas siguen el reemplazo normal de maps. Los enlaces de bucles y filtros quedan en su entrada.
Practica en By Example · Programa fuente
struct User { id: int, name: str, enabled: bool }
let users = [
User { id: 1, name: "Ada", enabled: true },
User { id: 2, name: "Lin", enabled: false },
]
let names = #{for user in users => [user.id]: user.name}
let active_names = #{for user in users => if user.enabled => [user.id]: user.name}
print(names[2])
print(active_names[1])Proyección tipada de records¶
user.{ id, name } evalúa user una vez y crea un record anónimo con los campos físicos visibles seleccionados. Conserva tipos exactos. Se rechazan selecciones repetidas, campos inaccesibles o calculados y copias lineales prohibidas.
Practica en By Example · Programa fuente
// Typed projection selects visible physical fields into an anonymous record.
struct User { id: int, name: str, internal: str }
var acquired = 0
fn make() -> User { acquired += 1; User { id: acquired, name: "Ada", internal: "hidden" } }
let public_user = make().{ name, id }
print(acquired)
print(public_user.id)
print(public_user.name)
let updated = public_user with { id += 4 }
print(public_user.id)
print(updated.id)
let empty = make().{}
print(acquired)
struct Box<T> { value: T, id: int }
macro selected(value) { $value.{ value } }
let integer_box: Box<int> = Box { value: 7, id: 1 }
let text_box: Box<str> = Box { value: "text", id: 2 }
let integer = selected!(integer_box)
let text = selected!(text_box)
print(integer.value)
print(text.value)
fn first() -> int { let unused = ({ return 8; User { id: 0, name: "", internal: "" } }).{ id }; 0 }
print(first())
var total = 0
:outer for n in 0..3 {
let value = ({ if n == 1 { continue :outer }; User { id: n, name: "", internal: "" } }).{ id }
total += value.id
}
print(total)Actualizaciones anidadas agrupadas¶
Un grupo dentro de una actualización with comparte el prefijo de la ruta: address { city: value, visits += 1 } actualiza address.city y address.visits. Se conservan las reglas de copia, privacidad y conflictos. Los inicializadores se ejecutan en el orden escrito.
// Update several fields under the same path, leaving the original value intact.
struct Address { city: str, visits: int }
struct Person { name: str, address: Address }
let original = Person { name: "Ana", address: Address { city: "Recife", visits: 2 } }
let moved = original with {
address { city: "Natal", visits += 1 },
}
print(original.address.city) // expect: Recife
print(moved.address.city) // expect: Natal
print(moved.address.visits) // expect: 3Rutas y alias en proyecciones¶
value.{ campo, ruta.campo as nombre } copia los campos almacenados seleccionados a un record anónimo. La base se evalúa una vez. Cada ruta debe ser visible y seguir las reglas de copia existentes. Los accesores calculados no pueden proyectarse y los nombres de salida deben ser únicos.
// Prepare a public response without copying internal fields.
struct Address { city: str, delivery_code: int }
struct User { id: int, name: str, address: Address }
let user = User {
id: 7,
name: "Ada",
address: Address { city: "Recife", delivery_code: 42 }
}
let response = user.{ id, name as display_name, address.city as city }
print("{response.id}: {response.display_name}, {response.city}")Patrones de array con sufijo¶
Un ...rest puede aparecer entre los elementos fijos. El prefijo cuenta desde el inicio; el sufijo, desde el final. El resto es un nuevo array y puede estar vacío. La entrada debe tener espacio para el prefijo y el sufijo.
// Read a framed packet without copying the header into its payload.
let packet = [170, 4, 8, 12, 255]
match packet {
[170, ...payload, 255] => {
print("payload: {payload}")
print("bytes: {payload.len()}")
},
_ => print("invalid frame")
}Patrones de map¶
Cada clave seleccionada debe existir antes de comprobar su valor. Las claves son strings literales; los valores admiten patrones anidados. Se permiten entradas adicionales y ...options copia las no seleccionadas a un nuevo map. Un valor nil elimina la entrada, por lo que cuenta como ausencia. Un patrón de map no coincide con un record anónimo.
// Decode a command and retain options that belong to the handler.
let command = #{"action": "rename", "name": "Ada", "mode": "safe"}
match command {
#{"action": "rename", "name": name, ...options} => {
print("rename to {name}")
print("mode: {options["mode"]}")
},
_ => print("unsupported command")
}Sigue aprendiendo¶
DOCS / OPINIÓN
¿Esta página dejó alguna pregunta?
Cuéntanos dónde te perdió la explicación. La documentación también forma parte de la experiencia del lenguaje.