@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 tipobool.
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
- Contagem sem branches: Somar condições verdadeiras sem usar
if, ideal para loops de alto desempenho onde a previsão de branch é custosa. - Programação branchless: Eliminar instruções de branch condicional para melhorar desempenho em código SIMD ou pipelines sensíveis.
- Indexação condicional: Usar o resultado como índice (0 ou 1) em arrays de dois elementos.
- Acumuladores: Incrementar contadores condicionalmente de forma compacta.
- 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
- @intFromEnum — Converte enum para inteiro
- @intFromFloat — Converte float para inteiro
- @intFromPtr — Converte ponteiro para inteiro
- @as — Conversão de tipo genérica