Skip to content
LogoLogo

Records

A record is a struct. Record literals use { field = value; }, and stored records can be updated one field at a time.

records.lithe
(* Records are structs. Storage lays them out exactly as Solidity would. *)
 
type Todo is record {
  priority : uint8;
  done : bool;
}
 
type Point is record {
  x : uint128;
  y : uint128;
}
 
storage is record {
  todos : map(uint, Todo);
  count : uint;
  origin : Point;
}
 
external function add(const priority : uint8) is {
  storage.todos[storage.count] := { priority = priority; done = false; };
  storage.count := storage.count + 1;
}
 
(* Write one field of a record stored in a map. *)
external function toggle(const id : uint) is {
  storage.todos[id].done := not storage.todos[id].done;
}
 
external view function get(const id : uint) : Todo is
  storage.todos[id]
 
external function move(const x : uint128; const y : uint128) is {
  storage.origin.x := x;
  storage.origin.y := y;
}
 
external view function x() : uint128 is
  storage.origin.x
 
(* A function can return a record, like any other value. *)
pure function swapped(const p : Point) : Point is { x = p.y; y = p.x; }
 
external function reflect() is {
  storage.origin := swapped(storage.origin);
}
 
external view function reflectedX() : uint128 is
  swapped(storage.origin).x
  • A record literal takes its type from context: a storage slot, an annotation, or a parameter.
  • get returns a record, which the ABI flattens into named outputs (uint8 priority, bool done), like a Solidity struct getter.
  • Records inside maps must be made of word-sized fields, with no string or bytes.
  • swapped returns a record. reflect stores the whole result at once, and reflectedX reads one field straight off the call. A whole-record store builds the new value before writing any of it, so swapped sees the old origin.