expected return expression — Como Resolver em Zig

expected return expression — Como Resolver em Zig

O Que Este Erro Significa

O erro expected return expression ocorre quando uma função em Zig declara um tipo de retorno diferente de void, mas o compilador detecta que existe um caminho de execução que termina sem uma expressão return adequada. Zig exige que todas as funções com tipo de retorno não-void retornem explicitamente um valor em todos os caminhos possíveis.

Causas Comuns

1. Função Sem Return

fn calcular(x: i32) i32 {
    const resultado = x * 2;
    // ERRO: função termina sem return
    _ = resultado;
}

2. Return Apenas em Alguns Branches

fn classificar(n: i32) []const u8 {
    if (n > 0) {
        return "positivo";
    } else if (n < 0) {
        return "negativo";
    }
    // ERRO: e se n == 0? Não há return para esse caso
}

3. Switch Sem Cobertura Completa

const Cor = enum { vermelho, verde, azul };

fn nomeCor(c: Cor) []const u8 {
    switch (c) {
        .vermelho => return "vermelho",
        .verde => return "verde",
        // ERRO: falta o caso .azul
    }
}

4. Loop que Pode Não Executar

fn encontrar(arr: []const u32, alvo: u32) u32 {
    for (arr) |item| {
        if (item == alvo) return item;
    }
    // ERRO: e se o array estiver vazio ou o item não for encontrado?
}

5. Confundir void com Tipo de Retorno

fn processar(dados: []const u8) []const u8 {
    if (dados.len == 0) {
        return; // ERRO: return sem valor em função que retorna []const u8
    }
    return dados;
}

Como Corrigir

Solução 1: Adicionar Return Explícito

fn calcular(x: i32) i32 {
    const resultado = x * 2;
    return resultado; // Correto: retorna o valor
}

Solução 2: Cobrir Todos os Branches

fn classificar(n: i32) []const u8 {
    if (n > 0) {
        return "positivo";
    } else if (n < 0) {
        return "negativo";
    } else {
        return "zero"; // Correto: todos os caminhos cobertos
    }
}

Solução 3: Usar else em Switch

const Cor = enum { vermelho, verde, azul };

fn nomeCor(c: Cor) []const u8 {
    return switch (c) {
        .vermelho => "vermelho",
        .verde => "verde",
        .azul => "azul", // Correto: todos os casos cobertos
    };
}

Se você quer garantir exaustividade sem usar else:

fn nomeCor(c: Cor) []const u8 {
    return switch (c) {
        .vermelho => "vermelho",
        .verde => "verde",
        .azul => "azul",
        // Se um novo valor for adicionado ao enum, este switch
        // dará erro de compilação — forçando você a tratá-lo
    };
}

Solução 4: Adicionar Return Após Loop

fn encontrar(arr: []const u32, alvo: u32) ?u32 {
    for (arr) |item| {
        if (item == alvo) return item;
    }
    return null; // Correto: retorna null se não encontrado
}

Ou usando error:

const BuscaError = error{NaoEncontrado};

fn encontrar(arr: []const u32, alvo: u32) BuscaError!u32 {
    for (arr) |item| {
        if (item == alvo) return item;
    }
    return BuscaError.NaoEncontrado;
}

Solução 5: Usar unreachable Quando Apropriado

Se você tem certeza absoluta de que um caminho nunca será alcançado:

fn dividir(a: u32, b: u32) u32 {
    if (b != 0) {
        return a / b;
    }
    unreachable; // Você garante que b nunca será 0
}

Cuidado: unreachable causa comportamento indefinido se for alcançado. Use apenas quando realmente puder garantir que o caminho é impossível.

Padrões Recomendados

Expressão Switch Como Retorno

Em vez de return em cada braço, use switch como expressão:

fn obterPrioridade(nivel: u8) []const u8 {
    return switch (nivel) {
        0 => "baixa",
        1 => "média",
        2 => "alta",
        3 => "crítica",
        else => "desconhecida",
    };
}

Expressão if Como Retorno

fn absoluto(n: i32) u32 {
    return if (n >= 0) @intCast(n) else @intCast(-n);
}

Retorno Antecipado (Early Return)

O padrão de retorno antecipado é idiomático em Zig:

fn processar(dados: ?[]const u8) []const u8 {
    const d = dados orelse return "vazio"; // Retorno antecipado
    if (d.len == 0) return "vazio";
    return d;
}

Diferença Entre void e noreturn

  • void: A função não retorna nenhum valor, mas retorna o controle ao chamador
  • noreturn: A função nunca retorna (ex: loop infinito, @panic, exit)
fn saudacao() void {
    // OK sem return — tipo de retorno é void
    std.debug.print("Olá!\n", .{});
}

fn falhar() noreturn {
    @panic("erro fatal"); // Nunca retorna
}

Erros Relacionados

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.