Guards e retorno antecipado
Nesta página
Retornar cedo quando uma pré-condição falha é a forma mais legível de validação
em Zolo: verifique no topo, retorne Result.Err(...) imediatamente, e o resto
da função executa apenas no caminho feliz — sem if/else aninhado.
Combinado com ?, um pipeline de validações fica linear:
Validação com retorno antecipado, pipeline com ?, e opcional com 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)
O padrão create_user mostra o núcleo da técnica: cada validador retorna
Result<_, str>, e ? propaga o primeiro erro encontrado sem acumular
match aninhado. O chamador vê um único ponto de falha descritivo.
Use guards para invariantes de entrada — dados que chegam do mundo externo (formulários, APIs, arquivos). Dentro da lógica interna, onde você controla os tipos, prefira
panicpara contratos quebrados.
Desafio
Adicione uma terceira validação a create_user que rejeite emails mais longos
que 100 caracteres. Teste com uma string longa e verifique que o erro correto
é propagado.