integer overflow at runtime — Como Resolver em Zig

integer overflow at runtime — Como Resolver em Zig

O Que Este Erro Significa

O erro integer overflow em tempo de execução é um panic que ocorre quando uma operação aritmética produz um resultado que não cabe no tipo inteiro utilizado. Em builds Debug e ReleaseSafe, Zig verifica automaticamente todas as operações aritméticas para overflow e gera um panic quando detectado. Isso previne uma classe inteira de bugs silenciosos que existem em C/C++.

A mensagem de panic é:

thread 1 panic: integer overflow

Diferença do Overflow em Comptime

O overflow em comptime é sempre um erro de compilação. O overflow em runtime só é detectado em builds com verificações de segurança habilitadas (Debug e ReleaseSafe). Em ReleaseFast e ReleaseSmall, a verificação é removida para performance.

Causas Comuns

1. Soma que Excede o Limite

pub fn main() void {
    var a: u8 = 250;
    a += 10; // PANIC: 250 + 10 = 260, que não cabe em u8 (max 255)
}

2. Multiplicação com Resultado Grande

pub fn main() void {
    var x: u16 = 1000;
    x *= 100; // PANIC: 1000 * 100 = 100000, não cabe em u16 (max 65535)
}

3. Subtração com Resultado Negativo em Unsigned

pub fn main() void {
    var saldo: u32 = 100;
    const debito: u32 = 150;
    saldo -= debito; // PANIC: 100 - 150 = -50, não representável em u32
}

4. Conversão com @intCast que Não Cabe

pub fn main() void {
    const grande: u32 = 300;
    const pequeno: u8 = @intCast(grande); // PANIC: 300 não cabe em u8
    _ = pequeno;
}

5. Loop com Contador que Estoura

pub fn main() void {
    var contador: u8 = 0;
    while (true) {
        contador += 1; // PANIC quando contador chega a 255 e tenta incrementar
        if (contador == 0) break; // Nunca alcançado em Debug
    }
}

6. Negação de Inteiro Mínimo em Signed

pub fn main() void {
    var x: i8 = -128;
    x = -x; // PANIC: -(-128) = 128, que não cabe em i8 (max 127)
}

Como Corrigir

Solução 1: Usar Tipo Maior

pub fn main() void {
    var x: u32 = 1000; // u32 ao invés de u16
    x *= 100; // OK: 100000 cabe em u32
    _ = x;
}

Solução 2: Operações com Saturação (+|, -|, *|)

Operações saturantes limitam o resultado ao valor máximo ou mínimo do tipo:

pub fn main() void {
    var a: u8 = 250;
    a +|= 10; // Resultado: 255 (saturado no máximo)
    // a é 255, não causa panic

    var b: u8 = 5;
    b -|= 10; // Resultado: 0 (saturado no mínimo)
    _ = a;
    _ = b;
}

Solução 3: Operações com Wrapping (+%, -%, *%)

Se o comportamento de wrap-around é o desejado:

pub fn main() void {
    var contador: u8 = 250;
    contador +%= 10; // Resultado: 4 (wrap around: 260 % 256 = 4)
    _ = contador;
}

Solução 4: Verificar Antes de Operar

const std = @import("std");

pub fn main() void {
    var saldo: u32 = 100;
    const debito: u32 = 150;

    if (debito > saldo) {
        std.debug.print("Saldo insuficiente!\n", .{});
    } else {
        saldo -= debito; // Seguro: verificado antes
    }
    _ = saldo;
}

Solução 5: Usar @addWithOverflow

const std = @import("std");

pub fn main() void {
    const a: u8 = 250;
    const b: u8 = 10;
    const resultado = @addWithOverflow(a, b);
    if (resultado[1] != 0) {
        std.debug.print("Overflow detectado!\n", .{});
    } else {
        std.debug.print("Resultado: {}\n", .{resultado[0]});
    }
}

Solução 6: Usar math.add Para Verificação Segura

const std = @import("std");

pub fn main() void {
    const a: u8 = 250;
    const b: u8 = 10;
    const resultado = std.math.add(u8, a, b) catch {
        std.debug.print("Overflow!\n", .{});
        return;
    };
    std.debug.print("Resultado: {}\n", .{resultado});
}

Operadores Aritméticos do Zig — Referência Rápida

OperaçãoNormalWrappingSaturating
Soma++%+|
Subtração--%-|
Multiplicação**%*|
Shift left<<<<%<<|

Comportamento por Build Mode

Build ModeOverflow detectado?Ação
DebugSimPanic + trace
ReleaseSafeSimPanic + trace
ReleaseFastNãoWrap silencioso
ReleaseSmallNãoWrap silencioso

Dicas Práticas

  1. Sempre teste em Debug — Overflow silencioso em Release pode esconder bugs
  2. Prefira tipos maiores — Se não há restrição de memória, use u32 ou u64
  3. Use saturação para contadores — Contadores que não devem ultrapassar um limite
  4. Use wrapping para checksums — Operações onde wrap-around é o comportamento desejado

Erros Relacionados

Continue aprendendo Zig

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