unreachable code — Como Resolver em Zig
O Que Este Erro Significa
O erro unreachable code ocorre quando o compilador Zig detecta que existe código no seu programa que nunca poderá ser executado. Isso geralmente acontece quando há instruções após um return, break, continue, unreachable ou @panic. Zig considera código morto como um erro de compilação, não apenas um aviso, porque indica quase sempre um erro de lógica no programa.
Causas Comuns
1. Código Após return
const std = @import("std");
fn exemplo() u32 {
return 42;
std.debug.print("isso nunca executa\n", .{}); // ERRO: unreachable code
}
2. Código Após break
const std = @import("std");
pub fn main() void {
while (true) {
break;
std.debug.print("nunca chega aqui\n", .{}); // ERRO: unreachable code
}
}
3. Código Após continue
pub fn main() void {
var i: u32 = 0;
while (i < 10) : (i += 1) {
if (i % 2 == 0) {
continue;
i += 10; // ERRO: unreachable code
}
}
}
4. Código Após @panic
const std = @import("std");
fn falhar() void {
@panic("erro fatal");
std.debug.print("não executa\n", .{}); // ERRO: unreachable code
}
5. Return em Todos os Branches Seguido de Código
fn classificar(n: i32) []const u8 {
if (n > 0) {
return "positivo";
} else {
return "não-positivo";
}
return "nunca chega aqui"; // ERRO: unreachable code
}
6. Switch Exaustivo Seguido de Return
const Dir = enum { norte, sul, leste, oeste };
fn nome(d: Dir) []const u8 {
return switch (d) {
.norte => "norte",
.sul => "sul",
.leste => "leste",
.oeste => "oeste",
};
return "impossível"; // ERRO: unreachable code (switch é exaustivo)
}
Como Corrigir
Solução 1: Remover o Código Morto
A solução mais simples — remova o código que nunca será executado:
fn exemplo() u32 {
return 42;
// Código morto removido
}
Solução 2: Reorganizar a Lógica
Se o código deveria ser alcançável, mova-o antes do ponto de saída:
const std = @import("std");
fn exemplo() u32 {
std.debug.print("calculando...\n", .{}); // Agora executa antes do return
return 42;
}
Solução 3: Mover Código Para Fora do Loop
const std = @import("std");
pub fn main() void {
while (true) {
break;
}
// Código que precisa executar após o loop vai aqui fora
std.debug.print("loop terminou\n", .{});
}
Solução 4: Usar Condição Correta
Se o código deveria ser condicional:
fn processar(valor: i32) i32 {
if (valor < 0) {
return -valor;
}
// Este código é alcançável quando valor >= 0
return valor * 2;
}
Solução 5: Usar noreturn Corretamente
Se uma função realmente nunca retorna, declare-a como noreturn:
fn sairComErro(msg: []const u8) noreturn {
std.debug.print("Erro: {s}\n", .{msg});
std.process.exit(1);
}
unreachable vs Código Inalcançável
Em Zig, existe uma diferença importante entre:
- Código inalcançável (erro de compilação): Código que o compilador detecta que nunca será executado
unreachable(palavra-chave): Uma asserção do programador de que um ponto no código nunca será alcançado
fn dividir_seguro(a: u32, b: u32) u32 {
// b é garantidamente != 0 pela lógica do programa
if (b == 0) unreachable; // Asserção — se alcançado em Debug, causa panic
return a / b;
}
unreachable é útil quando você sabe que algo é impossível mas o compilador não consegue provar. Em builds de Debug, alcançar unreachable causa um panic. Em builds Release, é comportamento indefinido.
Padrões Comuns que Evitam o Erro
Early Return
fn processar(dados: ?[]const u8) []const u8 {
if (dados == null) return "vazio";
// Código principal aqui — é alcançável
return dados.?;
}
Guard Clauses
fn validar(valor: i32) !i32 {
if (valor < 0) return error.Negativo;
if (valor > 1000) return error.MuitoGrande;
// Resto da lógica — alcançável quando valor está entre 0 e 1000
return valor;
}
Erros Relacionados
- expected return expression — Falta return em função não-void
- expected expression — Expressão esperada pelo compilador
- unused local variable — Variável declarada mas não utilizada