Optional em Zig — O que é e Como Usar
Definição
Um Optional em Zig é um tipo representado pelo operador ? que pode conter ou um valor válido do tipo T ou null. A sintaxe ?T declara um optional do tipo T. Diferentemente de linguagens como C onde qualquer ponteiro pode ser null, em Zig a possibilidade de nulidade é explícita no sistema de tipos.
Optionals eliminam uma das maiores fontes de bugs em programação: o acesso a valores nulos sem verificação.
Por que Optionals Importam
- Null safety: O compilador obriga você a verificar se o valor existe antes de usá-lo.
- Expressividade: A assinatura da função comunica claramente que “pode não haver valor”.
- Zero custo: Para ponteiros,
?*Ttem o mesmo tamanho que*T(null é representado como 0). - Diferente de erro: Optional indica ausência, não falha. Use Error Union quando houver motivo da falha.
Exemplo Prático
Uso Básico
const std = @import("std");
fn encontrar(haystack: []const u8, needle: u8) ?usize {
for (haystack, 0..) |char, indice| {
if (char == needle) return indice;
}
return null; // Não encontrou
}
pub fn main() void {
const texto = "Zig Brasil";
if (encontrar(texto, 'B')) |indice| {
std.debug.print("Encontrado na posição {}\n", .{indice});
} else {
std.debug.print("Não encontrado\n", .{});
}
}
Unwrap com orelse
const valor: ?u32 = null;
const resultado = valor orelse 42; // resultado = 42
const outro: ?u32 = 10;
const resultado2 = outro orelse 42; // resultado2 = 10
Unwrap com if
fn processar(talvez_dados: ?[]const u8) void {
if (talvez_dados) |dados| {
// dados é []const u8 — desembrulhado, sem null
std.debug.print("Dados: {s}\n", .{dados});
} else {
std.debug.print("Sem dados\n", .{});
}
}
Unwrap com while
fn proximoValor(iterador: *Iterador) ?u32 {
// retorna null quando acabar
}
// while com optional — itera até null
while (proximoValor(&iter)) |valor| {
std.debug.print("{}\n", .{valor});
}
Optional de Ponteiro
const Node = struct {
valor: i32,
proximo: ?*Node, // Ponteiro que pode ser null
};
fn ultimo(node: *Node) *Node {
var atual = node;
while (atual.proximo) |prox| {
atual = prox;
}
return atual;
}
Encadeamento com .?
const config: ?*Config = obterConfig();
// .? é equivalente a unwrap forçado (panic se null)
const porta = config.?.porta;
Cuidado: .? causa panic se o valor for null. Prefira if ou orelse.
Optional vs Error Union
| Característica | ?T (Optional) | !T (Error Union) |
|---|---|---|
| Significa | Valor pode estar ausente | Operação pode falhar |
| Informação extra | Nenhuma (só null) | Qual erro ocorreu |
| Operador | orelse | catch |
| Exemplo | Busca em lista | Leitura de arquivo |
Armadilhas Comuns
- Usar
.?sem verificação: O unwrap forçado (.?) causa panic se o valor for null. Useifouorelsepara segurança. - Confundir
?Tcom!T: Optional indica ausência; error union indica falha com motivo. Escolha o tipo correto. - Optionals aninhados:
??Té válido mas raramente útil e pode causar confusão. Repense o design se chegar nesse ponto. - Usar optional quando um valor default é melhor: Se sempre há um valor razoável de fallback, considere usar o valor diretamente em vez de optional.
Termos Relacionados
- Orelse — Operador para desembrulhar optionals
- Error Union — Tipo para erros com informação
- Pointer Types — Tipos de ponteiro em Zig
- Undefined — Valor indefinido