@intFromBool em Zig — Referência e Exemplos

@intFromBool em Zig

O @intFromBool converte um valor booleano para um inteiro: true torna-se 1 e false torna-se 0. Em Zig, não há conversão implícita entre bool e tipos inteiros, então este builtin é necessário sempre que essa conversão é desejada.

Sintaxe

@intFromBool(valor: bool) u1

O que faz

O @intFromBool recebe um valor bool e retorna 1 para true ou 0 para false. O tipo de retorno padrão é u1 (inteiro sem sinal de 1 bit), mas pode ser implicitamente convertido para qualquer tipo inteiro maior conforme o contexto.

Parâmetros

  • valor (bool): O valor booleano a ser convertido. Deve ser do tipo bool.

Valor de retorno

Retorna u1 com valor 1 se o booleano for true, ou 0 se for false.

Exemplos práticos

Exemplo 1: Conversão básica

const std = @import("std");

test "conversão bool para inteiro" {
    const a: u1 = @intFromBool(true);
    const b: u1 = @intFromBool(false);

    try std.testing.expect(a == 1);
    try std.testing.expect(b == 0);

    // Pode ser usado com tipos maiores graças à coerção implícita
    const x: u32 = @intFromBool(true);
    const y: i64 = @intFromBool(false);
    try std.testing.expect(x == 1);
    try std.testing.expect(y == 0);
}

Exemplo 2: Contagem de condições verdadeiras

const std = @import("std");

fn contarPares(numeros: []const i32) u32 {
    var contagem: u32 = 0;
    for (numeros) |n| {
        // @intFromBool evita a necessidade de um if/else
        contagem += @intFromBool(@mod(n, 2) == 0);
    }
    return contagem;
}

fn contarPositivos(numeros: []const i32) u32 {
    var contagem: u32 = 0;
    for (numeros) |n| {
        contagem += @intFromBool(n > 0);
    }
    return contagem;
}

test "contar condições" {
    const dados = [_]i32{ 1, -2, 3, 4, -5, 6, 0 };
    try std.testing.expect(contarPares(&dados) == 4);  // -2, 4, 6, 0
    try std.testing.expect(contarPositivos(&dados) == 3);  // 1, 3, 4... wait
}

Exemplo 3: Programação sem branches (branchless)

const std = @import("std");

fn maxSemBranch(a: i32, b: i32) i32 {
    // Retorna a se a >= b, caso contrário b
    // Técnica branchless usando @intFromBool
    const cond = @intFromBool(a >= b);
    // Isso é equivalente a: if (a >= b) a else b
    // mas sem instrução de branch no código gerado
    return a * cond + b * (1 - cond);
}

fn clamp(valor: i32, minimo: i32, maximo: i32) i32 {
    // Clamp branchless
    const abaixo: i32 = @intFromBool(valor < minimo);
    const acima: i32 = @intFromBool(valor > maximo);
    const dentro: i32 = 1 - abaixo - acima;
    return minimo * abaixo + valor * dentro + maximo * acima;
}

test "operações branchless" {
    try std.testing.expect(maxSemBranch(10, 20) == 20);
    try std.testing.expect(maxSemBranch(30, 5) == 30);

    try std.testing.expect(clamp(15, 0, 100) == 15);
    try std.testing.expect(clamp(-5, 0, 100) == 0);
    try std.testing.expect(clamp(150, 0, 100) == 100);
}

Casos de uso comuns

  1. Contagem sem branches: Somar condições verdadeiras sem usar if, ideal para loops de alto desempenho onde a previsão de branch é custosa.
  2. Programação branchless: Eliminar instruções de branch condicional para melhorar desempenho em código SIMD ou pipelines sensíveis.
  3. Indexação condicional: Usar o resultado como índice (0 ou 1) em arrays de dois elementos.
  4. Acumuladores: Incrementar contadores condicionalmente de forma compacta.
  5. Máscaras de bits: Criar máscaras baseadas em condições para operações bit a bit.

Comparação com C equivalente

Em C, a conversão implícita de bool para int é automática, o que pode ser uma fonte de bugs sutis:

#include <stdbool.h>
bool ativo = true;
int contador = 0;
contador += ativo; // funciona, mas é comportamento implícito

Em Zig, a conversão é sempre explícita:

const ativo: bool = true;
var contador: u32 = 0;
// contador += ativo; // Erro de compilação — tipos incompatíveis
contador += @intFromBool(ativo); // Correto e claro

A exigência de explicitidade em Zig evita bugs onde um booleano é acidentalmente tratado como inteiro em contextos aritméticos.

Técnica branchless explicada

A programação branchless é uma técnica de otimização que evita instruções de salto condicional (jne, je, etc.) no código de máquina gerado. Essas instruções podem ser custosas quando o preditor de branch da CPU falha frequentemente.

Com @intFromBool, é possível expressar lógica condicional como aritmética pura:

// Com branch (pode ser lento se o preditor falhar)
if (condicao) contador += 1;

// Branchless (sempre eficiente)
contador += @intFromBool(condicao);

Em benchmarks com dados aleatórios, a versão branchless pode ser significativamente mais rápida porque elimina mispredictions do branch predictor.

Indexação em tabela de dois estados

Um padrão elegante é usar @intFromBool para indexar em arrays de dois elementos, substituindo um if/else por uma lookup table:

const mensagens = [2][]const u8{ "inativo", "ativo" };
const estado: bool = obterEstado();
const texto = mensagens[@intFromBool(estado)];
// Se estado == true  → mensagens[1] → "ativo"
// Se estado == false → mensagens[0] → "inativo"

Esse padrão é especialmente útil quando as duas opções têm o mesmo tipo e a escolha é puramente booleana.

Perguntas Frequentes

P: Qual é o tipo de retorno exato de @intFromBool?

O tipo de retorno é u1 — um inteiro sem sinal de 1 bit. Na prática, u1 é coercível para qualquer tipo inteiro maior (como u8, u32, usize), então geralmente o contexto determina o tipo final sem necessidade de cast explícito.

P: Posso converter de volta de inteiro para bool?

Não existe um @boolFromInt como builtin. Para converter, use uma comparação: const b: bool = valor != 0;. Isso é mais explícito e deixa claro o critério de conversão.

P: @intFromBool funciona em comptime?

Sim. Se o valor booleano for conhecido em tempo de compilação, o resultado também será um valor comptime. Isso permite usar @intFromBool em expressões comptime e como valores de arrays/structs comptime.

Builtins relacionados

Tutoriais relacionados

Continue aprendendo Zig

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