Guards y retorno anticipado
En esta página
Retornar anticipadamente cuando una precondición falla es la forma más legible de
validación en Zolo: verifica al inicio, retorna Result.Err(...) de inmediato,
y el resto de la función se ejecuta solo en el camino feliz — sin if/else
anidado.
Combinado con ?, un pipeline de validaciones queda lineal:
Validación con retorno anticipado, pipeline con ?, y opcional con retorno nil.
// Feature: Early-return and argument validation
// Syntax: `if cond { return ... }` at the start of the function
// When to use: validate preconditions and simplify the happy path.
use std::Result
// -- Validation as Result — no panic ---------------------------
fn validate_age(n: int) -> Result<int, str> {
if n < 0 {
return Result.Err("negative age")
}
if n > 150 {
return Result.Err("implausible age")
}
return Result.Ok(n)
}
fn describe(r: Result<int, str>) {
match r {
Result::Ok(v) => print("ok: {v}"),
Result::Err(e) => print("error: {e}"),
}
}
describe(validate_age(30)) // ok: 30
describe(validate_age(-1)) // error: negative age
describe(validate_age(200)) // error: implausible age
// -- Validation pipeline with `?` -----------------------------
fn validate_email(s: str) -> Result<str, str> {
if s.len() == 0 {
return Result.Err("empty email")
}
if !s.contains("@") {
return Result.Err("email missing @")
}
return Result.Ok(s)
}
fn validate_name(s: str) -> Result<str, str> {
if s.len() < 2 {
return Result.Err("name too short")
}
return Result.Ok(s)
}
struct User {
name: str,
email: str,
}
fn create_user(name: str, email: str) -> Result<User, str> {
let n = validate_name(name)? // propagates error
let e = validate_email(email)? // propagates error
return Result.Ok(User { name: n, email: e })
}
fn describe_user(r: Result<User, str>) {
match r {
Result::Ok(u) => print("user: {u.name} <{u.email}>"),
Result::Err(msg) => print("error: {msg}"),
}
}
describe_user(create_user("Alice", "alice@x.com"))
// expected: user: Alice <alice@x.com>
describe_user(create_user("A", "alice@x.com"))
// expected: error: name too short
describe_user(create_user("Alice", "invalid"))
// expected: error: email missing @
// -- Optional + early return ----------------------------------
fn first_vowel(s: str) -> str? {
for c in s.chars() {
if c == "a" || c == "e" || c == "i" || c == "o" || c == "u" {
return c
}
}
return nil
}
print(first_vowel("brown") ?? "(none)") // o
print(first_vowel("xyz") ?? "(none)") // (none)
El patrón create_user muestra el núcleo de la técnica: cada validador retorna
Result<_, str>, y ? propaga el primer error encontrado sin acumular match
anidado. El llamador ve un único punto de fallo descriptivo.
Usa guards para invariantes de entrada — datos que llegan del mundo externo (formularios, APIs, archivos). Dentro de la lógica interna, donde controlas los tipos, prefiere
panicpara contratos rotos.
Desafío
Agrega una tercera validación a create_user que rechace emails de más de 100
caracteres. Prueba con una cadena larga y verifica que el error correcto se
propaga.
Consulta también