Шаг 4. Сопоставление с образцом
Зачем#
match — это if/else, который умеет разбирать данные и при этом проверяется компилятором на полноту. Если вы забыли
вариант enum или тип из объединения, программа не соберётся. Именно поэтому в OxL match — главный способ работать с
enum, с результатами, которые могут быть ошибкой, и с опциональными значениями.
Образцы#
enum Cmd {
Move(dx: i32, dy: i32)
Say(text: str)
Quit
}
data User { name: str; age: i32 }
fn classify(n: i32) -> str =>
match n {
0 => "zero"
1..=9 => "digit" // диапазон
x if x < 0 => "negative" // охрана: образец + условие
_ => "big" // _ — всё остальное
}
fn run(c: Cmd) -> str =>
match c {
Move(0, 0) => "stay" // литералы внутри варианта
Move(dx, dy) => "move {dx},{dy}"
Say(t) => "say {t}"
Quit => "bye"
}
fn first(xs: List<i32>) -> str =>
match xs {
[] => "empty" // образцы списков
[x] => "one {x}"
[x, ..rest] => "{x} and {rest.len()} more"
}
fn main() io {
for n in [0, 5, -3, 100] { print("{classify(n)}") }
for c in [Cmd.Move(0, 0), Cmd.Move(2, 3), Cmd.Say("hi"), Cmd.Quit] { print(run(c)) }
print("{first([])} | {first([7])} | {first([1, 2, 3])}")
let u = User{ name: "Ann", age: 31 }
if let User{ name, age, .. } = u { // if let: разобрать data, если образец подходит
print("{name} is {age}")
}
let (a, _, c) = (1, 2, 3)
print("{a + c}")
}zero digit negative big stay move 2,3 say hi bye empty | one 7 | 1 and 2 more Ann is 31 4
Разбор#
- Образцы проверяются сверху вниз. Литерал (
0), диапазон (1..=9), заполнитель_, привязка имени (x) и охранаx if x < 0— всё это образцы. Move(0, 0)— образец варианта с литералами внутри;Move(dx, dy)— привязывает поля. Образцы можно вкладывать.[],[x],[x, ..rest]— образцы списка: пустой, из одного элемента, «голова и хвост».if let User{ name, age, .. } = u { … }— выполнить блок, только если образец подошёл;..пропускает остальные поля.matchобязан быть исчерпывающим: последняя ветка_или полный перебор вариантов.
Объединения и опциональные значения#
Тип A | B — значение одного из типов. T? — это сокращение для T | none. Разбирать объединение можно match с
образцом-типом (i32 n) или проверять оператором is: после if r is TooBig внутри ветки r уже имеет тип TooBig.
type Port = u16 where 1..=65535 // уточнённый тип: диапазон проверяется при создании
@error data TooBig { max: i32 }
fn describe(x: i32 | str | bool) -> str => // объединение: значение одного из типов
match x {
i32 n => "int {n}" // образец-тип с привязкой
str s => "text {s}"
bool b => "flag {b}"
}
fn limit(n: i32) -> i32 | TooBig {
if n > 100 { fail TooBig{ max: 100 } }
n
}
fn main() io {
print(describe(5))
print(describe("hi"))
print(describe(true))
let r = limit(500)
if r is TooBig { // is сужает тип в ветке
print("too big, max {r.max}")
}
let maybe: i32? = none
print("{maybe ?? 0} {maybe is none}")
let name: str? = "Ann"
print("{name?.len() ?? 0}") // ?. — вызов, если значение есть
match Port.from(80) {
Port => print("port ok")
InvalidValue => print("port rejected")
}
match Port.from(0) {
Port => print("port ok")
InvalidValue => print("port rejected")
}
}int 5 text hi flag true too big, max 100 0 true 3 port ok port rejected
Что здесь нового:
type Port = u16 where 1..=65535— уточнённый тип: значение проверяется при создании.Port.from(80)возвращаетPort | InvalidValue, аPort.from(0)—InvalidValue.name?.len()— безопасный вызов: если значения нет, результатnone, а?? 0подставляет ноль.- Ошибки — тоже обычные типы в объединении (
i32 | TooBig); подробнее — в следующем шаге.
Типичная ошибка: неполный match#
enum Role { User, Support, Admin }
fn label(r: Role) -> str {
match r {
Role.User => "user"
Role.Support => "support"
}
}non-exhaustive
Компилятор сообщает, какой вариант не учтён: non-exhaustive. Исправление — добавить ветку
Role.Admin => … или _ => …. Предпочитайте явные ветки: тогда при добавлении нового варианта вы получите ошибку компиляции
вместо молчаливого попадания в _. Ветка, которая никогда не сработает, помечается unreachable-arm.
Что запомнить#
match— выражение, всегда исчерпывающее.- Образцы: литералы, диапазоны, варианты,
data, кортежи, списки, альтернативыA | B, охраныif. isиmatchсужают тип;?.и??работают сT?.
Дальше: ошибки.