Signed/Unsigned Mismatch — Como Resolver em Zig

Signed/Unsigned Mismatch — Como Resolver em Zig

O Que Este Erro Significa

O erro de signed/unsigned mismatch ocorre quando o código tenta usar um tipo inteiro com sinal (signed, como i32) onde um tipo sem sinal (unsigned, como u32) é esperado, ou vice-versa. Zig não faz conversão automática entre tipos signed e unsigned porque essa conversão pode perder informação ou mudar o significado do valor.

Mensagens típicas do compilador:

error: expected type 'u32', found 'i32'
error: signed-unsigned mismatch in operands

Em C, essa conversão é feita silenciosamente e é fonte de inúmeros bugs. Zig exige que o programador seja explícito sobre suas intenções.

Causas Comuns

1. Atribuir Valor Signed a Variável Unsigned

pub fn main() void {
    const a: i32 = 42;
    const b: u32 = a; // ERRO: i32 pode ser negativo
    _ = b;
}

2. Passar Argumento de Tipo Errado

fn processar(indice: usize) void {
    _ = indice;
}

pub fn main() void {
    const i: i32 = 5;
    processar(i); // ERRO: esperado usize, recebeu i32
}

3. Comparar Signed com Unsigned

pub fn main() void {
    const a: i32 = -1;
    const b: u32 = 5;
    const resultado = a < b; // ERRO: não pode comparar i32 com u32 diretamente
    _ = resultado;
}

4. Operação Aritmética entre Tipos Misturados

pub fn main() void {
    const a: i32 = 10;
    const b: u32 = 5;
    const soma = a + b; // ERRO: operandos de tipos diferentes
    _ = soma;
}

5. Índice de Array com Tipo Signed

pub fn main() void {
    const arr = [_]u8{ 1, 2, 3 };
    const i: i32 = 1;
    const val = arr[i]; // ERRO: índice deve ser usize
    _ = val;
}

Como Corrigir

Solucao 1: Usar @intCast (Quando o Valor É Garantidamente Válido)

pub fn main() void {
    const a: i32 = 42;
    const b: u32 = @intCast(a); // OK se a >= 0, PANIC se a < 0
    _ = b;
}

Solucao 2: Verificar Antes de Converter

const std = @import("std");

pub fn main() void {
    const a: i32 = -5;
    if (a >= 0) {
        const b: u32 = @intCast(a);
        std.debug.print("Convertido: {}\n", .{b});
    } else {
        std.debug.print("Valor negativo: {}\n", .{a});
    }
}

Solucao 3: Usar std.math.cast para Conversão Segura

const std = @import("std");

pub fn main() void {
    const a: i32 = -5;
    const b: ?u32 = std.math.cast(u32, a);
    const valor = b orelse {
        std.debug.print("Não pode converter {} para u32\n", .{a});
        return;
    };
    std.debug.print("Convertido: {}\n", .{valor});
}

Solucao 4: Uniformizar Tipos na Declaração

pub fn main() void {
    // Use o mesmo tipo desde o início
    const a: u32 = 42;
    const b: u32 = 10;
    const soma = a + b; // Sem conflito de tipos
    _ = soma;
}

Solucao 5: Converter para Tipo Comum Antes de Operar

pub fn main() void {
    const a: i32 = 10;
    const b: u32 = 5;

    // Converte ambos para um tipo que suporte os dois
    const a_64: i64 = a;
    const b_64: i64 = @intCast(b);
    const soma = a_64 + b_64;
    _ = soma;
}

Solucao 6: @bitCast para Reinterpretação de Bits

pub fn main() void {
    // Quando quer o mesmo padrão de bits com outro tipo
    const a: i32 = -1;
    const b: u32 = @bitCast(a); // b == 4294967295 (0xFFFFFFFF)
    _ = b;

    // E vice-versa
    const c: u32 = 4294967295;
    const d: i32 = @bitCast(c); // d == -1
    _ = d;
}

Armadilhas Comuns em C (Evitadas por Zig)

Em C, a conversão silenciosa causa bugs sutis:

// C — bugs silenciosos
unsigned int a = 0;
int b = -1;
if (b < a) { // FALSO em C! -1 vira 4294967295
    printf("correto");
}

Em Zig, esse código simplesmente não compila, obrigando o programador a lidar com a questão:

pub fn main() void {
    const a: u32 = 0;
    const b: i32 = -1;
    // Zig: ERRO DE COMPILAÇÃO — comparação signed/unsigned
    // O programador DEVE decidir como tratar
    _ = a;
    _ = b;
}

Tabela de Conversões

DeParaMétodoRisco
i32 positivou32@intCastPanic se negativo
u32i32@intCastPanic se > MAX_i32
i32u32@bitCastReinterpreta bits
i32/u32i64Coerção automáticaNenhum (widening)
i32 qualquer?u32std.math.castRetorna null se inválido

Quando Usar Cada Tipo

  • u32, u64, usize: Contadores, índices, tamanhos, quantidades que nunca são negativas
  • i32, i64: Diferenças, coordenadas, valores que podem ser negativos
  • usize: Índices de array, tamanhos de memória (obrigatório para indexação)
pub fn main() void {
    // Padrão idiomático em Zig
    const tamanho: usize = 100;    // Tamanhos são sempre usize
    const indice: usize = 0;       // Índices são sempre usize
    const diferenca: i64 = -5;     // Diferenças podem ser negativas
    const contagem: u32 = 42;      // Contagens são unsigned
    _ = tamanho;
    _ = indice;
    _ = diferenca;
    _ = contagem;
}

Erros Relacionados

Continue aprendendo Zig

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