Шаг 3. Данные: data и enum
Зачем#
Хорошая модель данных делает невозможные состояния непредставимыми. Для этого в OxL два основных строительных блока:
data— запись: «и то, и другое, и третье» (произведение типов). Пользователь — это имя и возраст и email.enum— выбор: «или то, или другое» (сумма типов). Фигура — это круг или прямоугольник или точка, причём у каждого варианта свои поля.
Поведение (методы) пишется отдельно, в блоке impl, — так определение данных остаётся коротким и обозримым.
Пример#
@derive(Eq, Show)
data Point { x: i32; y: i32 } // data — запись с именованными полями
impl Point {
static fn origin() -> Point => Point{ x: 0, y: 0 } // static fn — «конструктор»
fn manhattan() -> i32 => x.abs() + y.abs() // поля доступны по имени
var fn moveBy(dx: i32, dy: i32) { // var fn изменяет значение
x += dx
y += dy
}
}
enum Shape { // enum — сумма вариантов
Circle(r: f64)
Rect(w: f64, h: f64)
Dot
}
fn describe(s: Shape) -> str =>
match s {
Circle(r) => "circle r={r}"
Rect(w, h) => "rect {w}x{h}"
Dot => "dot"
}
fn main() io {
var p = Point{ x: 3, y: -4 }
print("{p.manhattan()}")
p.moveBy(1, 1)
print("{p}")
print("{p == Point{ x: 4, y: -3 }} {Point.origin() == p}")
for s in [Shape.Circle(1.5), Shape.Rect(2.0, 3.0), Shape.Dot] {
print(describe(s))
}
}Вывод
7
Point{x: 4, y: -3}
true false
circle r=1.5
rect 2.0x3.0
dotРазбор#
data Point { x: i32; y: i32 }— поля разделяются;или переводом строки. Литерал записи:Point{ x: 3, y: -4 }. Все поля обязательны (если у поля нет значения по умолчанию).@derive(Eq, Show)просит компилятор сгенерировать сравнение==и вывод в строку{p}. Доступны такжеCopy,Ord,Hash.- В
impl Pointметоды видят поля по имени:x.abs() + y.abs().static fn origin()вызывается какPoint.origin(). var fn moveBy— метод, который изменяет значение; вызвать его можно только уvar-переменной.enum Shape { Circle(r: f64) … Dot }— варианты могут иметь поля или быть «пустыми». Создаются какShape.Circle(1.5).match s { … }разбирает вариант и достаёт поля. Подробно — в следующем шаге.
Типичные ошибки#
Забытое поле в литерале записи:
data User {
id: u64
name: str
}
fn main() {
let u = User{ id: 1 }
}error
missing-field
Диагностика missing-field называет недостающее поле. Опечатка в имени поля даёт
unknown-field.
Забытый вариант в match (см. следующий шаг) — non-exhaustive: добавили новый вариант в enum —
компилятор покажет все места, которые надо обновить.
Что запомнить#
data— «и»,enum— «или»; методы живут вimpl.@deriveизбавляет от шаблонного кода.- Добавление варианта в
enumломает сборку везде, гдеmatchне учитывает его, — это защита, а не неудобство.
Дальше: сопоставление с образцом.