Pular para o conteúdo

Guia 8 de 53

Condições e recuperação de erros

Condições, entradas de coleções, comparações encadeadas e fronteiras de Result.

Nesta página

Condições, entradas de coleções, comparações encadeadas e fronteiras de Result.

Condições com bindings

Condições separadas por vírgulas executam da esquerda para a direita, com curto-circuito. Bindings bem-sucedidos ficam disponíveis nas próximas condições e no corpo. Um guard que cria bindings precisa sair em todos os caminhos de falha.

Pratique na By Example · Programa fonte

// Feature: lazy comma conditions and binding guards.
// Each pattern publishes its bindings only after matching successfully.
enum Entry { Value(int), Missing }

fn positive(entry: Entry) -> int {
    guard let .Value(value) = entry, value > 0 else { return 0 }
    return value
}

print(positive(Entry.Value(7)))
print(positive(Entry.Value(-1)))

let value = 99
if let .Value(value) = Entry.Value(3), value > 10 {
    print(value)
} else {
    // Failure sees the outer binding, including failure of a later filter.
    print(value)
}

fn next(index: int) -> int? {
    if index <= 3 { return index }
    return nil
}

let mut index = 1
while let item = next(index), item < 3 {
    print(item)
    index += 1
}
print(index)
// expected:
// 7
// 0
// 99
// 1
// 2
// 3

Entradas condicionais em arrays

Use if condição => valor e for item in itens => valor dentro de um array. Entradas são produzidas na ordem escrita. Condições falsas omitem entradas; nil ainda ocupa uma posição. Arrays aninhados continuam sendo valores sem um spread explícito.

Pratique na By Example · Programa fonte

// Mix ordinary values, conditional entries and loops in one array.
struct Page { title: str, enabled: bool }

let admin = true
let pages = [
    Page { title: "Reports", enabled: true },
    Page { title: "Drafts", enabled: false },
]
let menu = [
    "Home",
    if admin => "Admin",
    for page in pages => if page.enabled => page.title,
]
for title in menu { print(title) }
// expected: Home
// expected: Admin
// expected: Reports

let positions = [for position, page in pages => position + 10]
print(positions[0])
// expected: 10
print(positions[1])
// expected: 11

let flattened = [0, for row in [[1, 2], [3]] => for cell in row => cell, 4]
print(flattened.len())
// expected: 5

let with_nil = [if true => nil, 7]
print(with_nil.len())
// expected: 2

Fronteiras locais de Result

attempt executa um bloco e envolve seu resultado normal em Ok. Um ? de Result que falha sai do attempt mais próximo como Err, permitindo continuar no chamador. Um Result no final fica aninhado. Panics não são capturados; propagação de opcionais exige conversão explícita do erro.

Pratique na By Example · Programa fonte

// Feature: local Result boundaries
// Syntax: attempt { let value = fallible()?; value }
// When to use: handle a Result failure locally and continue the caller.

use std::Result

fn read_number(text: str) -> Result<int, str> {
  if text == "42" { return Result.Ok(42) }
  return Result.Err("bad number")
}

fn main() {
  let good: Result<int, str> = attempt {
    let value = read_number("42")?
    value + 1
  }
  match good {
    Result::Ok(value) => print(value),
    Result::Err(error) => print(error),
  }
  // expected: 43

  let bad: Result<int, str> = attempt {
    let value = read_number("other")?
    value + 1
  }
  match bad {
    Result::Ok(value) => print(value),
    Result::Err(error) => print(error),
  }
  // expected: bad number
  print("caller continues")
  // expected: caller continues
}

main()

Comparações encadeadas

Escreva mínimo <= valor < máximo em uma cadeia. Cada operando compartilhado é avaliado uma vez, e uma comparação falsa interrompe a cadeia. Assim, checagens de intervalo não repetem uma expressão potencialmente cara.

Pratique na By Example · Programa fonte

// Chained comparisons reuse the middle value and stop on the first false pair.
var calls = 0
fn read(value: int) -> int { calls += 1; value }

let age = 18
print(0 <= age < 150) // expected: true
print(1 < 2 <= 2) // expected: true
print(4 > 2 >= 2) // expected: true
print(0 < read(2) < 3) // expected: true
print(calls) // expected: 1
calls = 0
print(5 < read(2) < read(9)) // expected: false
print(calls) // expected: 1
print(1d < 2d <= 3d) // expected: true
print("a" < "b" < "c") // expected: true

Recuperação de Result em expressões

recover avalia o Result uma vez e extrai Ok. Seus padrões correspondem ao conteúdo de Err e precisam cobrir esse tipo. Ramos que continuam retornam o mesmo tipo do sucesso. Retornos, propagação e limpeza continuam pertencendo ao escopo externo.

Pratique na By Example · Programa fonte

// A successful Result keeps its value. Recovery handles only its error.
enum ReadError { Missing, Invalid }

fn number(result: Result<int, ReadError>) -> int {
    result recover {
        .Missing => 0,
        .Invalid => -1,
    }
}

fn preserve(result: Result<int, str>) -> Result<int, str> {
    let value = result recover { error => return Result.Err(error) }
    Result.Ok(value)
}

fn main() {
    print(number(Result.Ok(7)))
    print(number(Result.Err(ReadError.Missing)))
    print(number(Result.Err(ReadError.Invalid)))
    print(preserve(Result.Err("missing")).is_err())
}

Bindings de padrões com is

Use is com um padrão de valor para testar e criar bindings em uma condição. Capturas ficam visíveis apenas no caminho bem-sucedido, incluindo condições seguintes. Testes existentes is Tipo e is not Tipo mantêm seus significados.

Pratique na By Example · Programa fonte

enum Reading { Value(int), Missing }

fn read(input: Reading) -> int {
    guard input is .Value(value) else { return 0 }
    value
}

let input: Reading = .Value(7)
if input is .Value(value) && value > 0 {
    assert(value == 7)
}
assert(read(input) == 7)
assert(read(Reading.Missing) == 0)
let values = [if input is .Value(value) => value]
let names = #{if input is .Value(value) => [value]: "selected"}
assert(values[0] == 7)
assert(names[7] == "selected")

Iteração filtrada com for case

for case padrão in valores ignora valores que não correspondem e cria bindings apenas no corpo bem-sucedido. Um for comum com padrão refutável mantém seu panic em caso de incompatibilidade. O iterável é avaliado uma vez e continue segue a regra normal do loop.

Pratique na By Example · Programa fonte

// Filtering loops evaluate their input once and skip non-matching elements.
var acquisitions = 0
fn results() -> [Result<int, str>] {
    acquisitions += 1;
    [.Ok(1), .Err("skip"), .Ok(2), .Ok(3)]
}
var total = 0
var events = ""
let __zolo_for_case_0 = 100
for case .Ok(value) in results() {
    assert(__zolo_for_case_0 == 100)
    defer { events += "{value}" }
    if value == 2 { continue }
    total += value
}
print(total) // expect: 4
print(acquisitions) // expect: 1
print(events) // expect: 123

let candidates: [Result<int,str>] = [.Ok(1), .Err("skip"), .Ok(2)]
var labels = 0
:outer for round in 0..3 {
    for case .Ok(value) in candidates {
        labels += 1
        if round == 0 && value == 1 { continue :outer }
        if round == 1 && value == 2 { break :outer }
    }
}
print(labels) // expect: 3

var hits = 0
for case 1 | 3 in [1, 2, 3] { hits += 1 }
print(hits) // expect: 2

let optional: [int?] = [1, nil, 3]
var wildcards = 0
var nils = 0
for case _ in optional { wildcards += 1 }
for case nil in optional { nils += 1 }
print(wildcards) // expect: 3
print(nils) // expect: 1

let options: [Option<int>] = [.Some(8), .None]
for case whole @ .Some(value) in options {
    let original: Option<int> = whole
    print(value) // expect: 8
    print(original.unwrap()) // expect: 8
}

// A matching body's linear resources still follow their own using scope.
@linear struct Resource { id: int }
impl core::cleanup::Cleanup for Resource {
    fn close(self) { print("close"); print(self.id) }
}
let resources: [Result<int,str>] = [.Ok(4), .Err("skip"), .Ok(5)]
for case .Ok(value) in resources {
    using resource = Resource { id: value } {
        if value == 4 { continue }
        break
    }
}
// expect: close
// expect: 4
// expect: close
// expect: 5

// Numeric range bounds are frozen before the body and end without overflow.
let maximum = 9223372036854775807
let minimum = -maximum - 1
let start = 0
var starts = 0
for case value in start..({ start = 3; 4 }) { starts += 1 }
print(starts) // expect: 4
let end = 4
var ends = 0
for case value in 0..end { end = 0; ends += 1 }
print(ends) // expect: 4
var singleton = 0
for case value in maximum..=maximum { singleton += 1; continue }
print(singleton) // expect: 1
var empty = 0
for case value in minimum..minimum { empty += 1 }
print(empty) // expect: 0
var opened = 0
for case value in maximum.. { assert(value == maximum); opened += 1; break }
print(opened) // expect: 1

// Iterator return stays in the caller and closes its enclosing resource.
fn header_return() -> int {
    let values: [Result<int, str>] = [.Ok(1)]
    using resource = Resource { id: 9 } {
        for case .Ok(item) in ({ return 7; values }) {}
    }
    99
}
print(header_return())
// expect: close
// expect: 9
// expect: 7

// Filtering strings uses Unicode scalar elements rather than UTF-8 bytes.
var unicode_hits = 0
var unicode_count = 0
var unicode_text = ""
for case "é" in "é🙂" { unicode_hits += 1 }
for case character in "é🙂" { unicode_count += 1; unicode_text += character }
print(unicode_hits)
print(unicode_count)
print(unicode_text)
macro count_elements(values) { var count = 0; for case _ in $values { count += 1 }; count }
print(count_elements!("é🙂"))
print(count_elements!([1, 2, 3]))

Filtragem em construtores de coleções

for case padrão in valores => entrada só adiciona uma entrada quando o padrão corresponde ao valor. Falhas de correspondência não executam a expressão da entrada. A forma funciona em arrays e maps; os nomes vinculados pelo padrão pertencem a cada entrada aceita.

// Failed readings produce neither an entry nor a body evaluation.
let readings: [Result<int, str>] = [.Ok(3), .Err("offline"), .Ok(5)]
var evaluated = 0
fn double(value: int) -> int { evaluated += 1; value * 2 }
let values = [for case .Ok(value) in readings => double(value)]
print(values[0]) // expect: 6
print(values[1]) // expect: 10
print(values.len()) // expect: 2
print(evaluated) // expect: 2
let indexed = #{for case .Ok(value) in readings => [value]: value * 2}
print(indexed[3]) // expect: 6
print(indexed[5]) // expect: 10

Continue aprendendo

Documentação · Pratique na By Example

DOCS / OPINIÃO

Esta página deixou alguma pergunta?

Conte onde a explicação perdeu você. A documentação também faz parte da experiência da linguagem.

Índice global

Encontre seu caminho pela Zolo

Experimente uma ideia

Comece por aqui

9 resultados

9 resultados

enespt-br