Saltar al contenido

Guía 8 de 53

Condiciones y recuperación de errores

Condiciones, entradas de colecciones, comparaciones encadenadas y límites de Result.

En esta página

Condiciones, entradas de colecciones, comparaciones encadenadas y límites de Result.

Condiciones con enlaces

Las condiciones separadas por comas se evalúan de izquierda a derecha con cortocircuito. Los enlaces exitosos están disponibles en las condiciones siguientes y en el cuerpo. Un guard con enlaces debe salir por todas las rutas de fallo.

Practica en By Example · Programa fuente

// 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 condicionales en arrays

Usa if condición => valor y for elemento in elementos => valor dentro de un array. Las entradas se producen en el orden escrito. Las condiciones falsas omiten entradas; nil ocupa una posición. Los arrays anidados siguen siendo valores sin un spread explícito.

Practica en By Example · Programa fuente

// 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

Límites locales de Result

attempt ejecuta un bloque y envuelve su resultado normal en Ok. Un ? de Result que falla sale del attempt más cercano como Err. Un Result al final permanece anidado. No captura panics; la propagación de opcionales requiere conversión explícita del error.

Practica en By Example · Programa fuente

// 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()

Comparaciones encadenadas

Escribe mínimo <= valor < máximo en una cadena. Cada operando compartido se evalúa una vez y una comparación falsa detiene la cadena. Las comprobaciones de intervalo no repiten una expresión potencialmente costosa.

Practica en By Example · Programa fuente

// 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

Recuperación de Result en expresiones

recover evalúa el Result una vez y extrae Ok. Sus patrones corresponden al contenido de Err y deben cubrir ese tipo. Las ramas que continúan devuelven el tipo del éxito. Retornos, propagación y limpieza pertenecen al alcance exterior.

Practica en By Example · Programa fuente

// 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())
}

Enlaces de patrones con is

Usa is con un patrón de valor para probar y enlazar en una condición. Las capturas son visibles solo en el camino exitoso, incluidas condiciones siguientes. Las pruebas existentes is Tipo e is not Tipo mantienen su significado.

Practica en By Example · Programa fuente

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")

Iteración filtrada con for case

for case patrón in valores omite valores que no coinciden y enlaza solo el cuerpo exitoso. Un for común con patrón refutable conserva su panic ante una incompatibilidad. El iterable se evalúa una vez y continue sigue la regla normal del bucle.

Practica en By Example · Programa fuente

// 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]))

Filtrado en constructores de colecciones

for case patrón in valores => entrada solo añade una entrada cuando el patrón coincide con el valor. Si no coincide, la expresión de la entrada no se ejecuta. La forma funciona en arrays y maps; los nombres vinculados por el patrón pertenecen a cada entrada aceptada.

// 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

Sigue aprendiendo

Documentación · Practica en By Example

DOCS / OPINIÓN

¿Esta página dejó alguna pregunta?

Cuéntanos dónde te perdió la explicación. La documentación también forma parte de la experiencia del lenguaje.

Índice global

Encuentra tu camino por Zolo

Prueba una idea

Empieza aquí

9 resultados

9 resultados

enespt-br