Saltar al contenido

panic, try/catch y catch_panic

En esta página

panic es para bugs y contratos rotos — situaciones que no deberían ocurrir si el código es correcto. A diferencia de Result, un panic interrumpe la ejecución de inmediato; no es un valor a propagar.

try / catch / finally permite capturar panics en una frontera controlada. finally se ejecuta siempre, con o sin pánico:

try/catch/finally, try como expresión, panic con interpolación, try anidado.

06-try-catch.zolo
Playground
// Feature: try / catch / finally

// Syntax: `try { ... } catch e { ... } finally { ... }`

// When to use: catch panics and ensure cleanup.


// -- Basic try / catch ----------------------------------------

try {
  panic("something went wrong!")
} catch e {
  print("caught!")
}

// expected: caught!

// (the variable `e` contains the message with the panic location info)


// -- try / catch / finally ------------------------------------

try {
  print("trying...")
  panic("oops")
} catch e {
  print("caught error")
} finally {
  print("cleanup always runs")
}

// expected:

// trying...

// caught error

// cleanup always runs


// finally runs even when there is NO panic.

try {
  print("ok")
} catch e {
  print("won't enter")
} finally {
  print("still runs")
}

// expected:

// ok

// still runs


// -- try as expression — returns a value ----------------------

let v = try { 42 } catch e { -1 }
print(v)  // 42


let f = try { panic("x"); 0 } catch e { 99 }
print(f)  // 99


// -- panic with interpolated message --------------------------

fn divide(a: int, b: int) -> int {
  if b == 0 {
    panic("Division by zero: {a} / {b}")
  }
  return a / b
}

let safe = try { divide(10, 0) } catch e { -1 }
print(safe)  // -1


print(try { divide(9, 3) } catch e { -1 })

// expected: 3 (exact division)


// -- Nested try/catch -----------------------------------------

try {
  try {
    panic("inner")
  } catch e {
    print("inner caught")
    panic("re-thrown")
  }
} catch e {
  print("outer caught")
}
// expected:

// inner caught

// outer caught

La variable e capturada por catch contiene el mensaje del pánico precedido por la ubicación en el código (archivo y línea) — no es solo la cadena que pasaste a panic.

Cuando necesitas tratar una función que puede entrar en pánico como si fuera Result, usa catch_panic. Retorna un objeto con .ok (bool) y .value (el retorno en caso de éxito). Para pasar argumentos, envuelve la llamada en un closure:

panic, catch_panic con y sin closure, Result.from_pcall para convertir panic en Result.

08-panic-and-catch.zolo
Playground
// Feature: panic and catch_panic

// Syntax: `panic("msg")`, `catch_panic(fn, args...)`

// When to use: non-recoverable errors (panic), safe wrapping (catch_panic).


use std::Result

// -- panic — interrupts execution ------------------------------

// When an invariant is violated and there's no reasonable recovery:

fn must_be_positive(n: int) -> int {
  if n < 0 {
    panic("n must be positive, got {n}")
  }
  return n
}

print(must_be_positive(5))  // 5


// Without catching, panic terminates the program. To catch:

let safe = try { must_be_positive(-3) } catch e { -1 }
print(safe)  // -1


// -- catch_panic — wraps a function call -----------------------

// Useful when you want to call an external function that may

// panic and treat the result as data.


fn risky() -> int {
  panic("boom")
  return 0
}

let res = catch_panic(risky)
if res.ok {
  print("value: {res.value}")
} else {
  print("caught panic")
}

// expected: caught panic


// Success:

fn safe_fn() -> int {
  return 42
}

let r2 = catch_panic(safe_fn)
if r2.ok {
  print("value: {r2.value}")
} else {
  print("caught panic")
}

// expected: value: 42


// -- catch_panic with a closure capturing arguments -----------

// To pass arguments to a function under catch_panic, wrap it in

// a zero-parameter closure.

fn divide_panic(a: int, b: int) -> int {
  if b == 0 {
    panic("division by zero")
  }
  return a / b
}

let ok = catch_panic(|| divide_panic(10, 2))
let err = catch_panic(|| divide_panic(10, 0))

print(ok.ok)  // true

print(ok.value)  // 5

print(err.ok)  // false


// -- Result.from_pcall — converts panic into Result -----------

fn require_positive(n: int) -> int {
  if n < 0 {
    panic("negative")
  }
  return n * 2
}

fn describe(r: Result<int, str>) {
  match r {
    Result::Ok(v) => print("ok: {v}"),
    Result::Err(_) => print("error caught"),
  }
}

describe(Result::from_pcall(require_positive, 5))
// expected: ok: 10

describe(Result::from_pcall(require_positive, -1))
// expected: error caught

Regla práctica: captura panics solo en fronteras (nivel superior de la aplicación, tests, wrappers de bibliotecas externas). Dentro de la lógica de negocio, prefiere Result<T, E>.

Buscar en Zolo

9 resultados

enespt-br