OxL Справочник OxL
РусскийEnglish

Шаг 4. Сопоставление с образцом

Зачем#

match — это if/else, который умеет разбирать данные и при этом проверяется компилятором на полноту. Если вы забыли вариант enum или тип из объединения, программа не соберётся. Именно поэтому в OxL match — главный способ работать с enum, с результатами, которые могут быть ошибкой, и с опциональными значениями.

Образцы#

oxl✓ проверяется в CI
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.

oxl✓ проверяется в CI
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#

oxl✓ проверяется в CI
enum Role { User, Support, Admin }

fn label(r: Role) -> str {
  match r {
    Role.User => "user"
    Role.Support => "support"
  }
}
error
non-exhaustive

Компилятор сообщает, какой вариант не учтён: non-exhaustive. Исправление — добавить ветку Role.Admin => … или _ => …. Предпочитайте явные ветки: тогда при добавлении нового варианта вы получите ошибку компиляции вместо молчаливого попадания в _. Ветка, которая никогда не сработает, помечается unreachable-arm.

Что запомнить#

  • match — выражение, всегда исчерпывающее.
  • Образцы: литералы, диапазоны, варианты, data, кортежи, списки, альтернативы A | B, охраны if.
  • is и match сужают тип; ?. и ?? работают с T?.

Дальше: ошибки.

См. также

OxL 1.0.0-beta · Версия документации 1.0.0-beta© OxwaldLink · сгенерировано из Стандарта, исходников std и руководства