Type Coercion em Zig — O que é e Como Usar
Definição
Type coercion (coerção de tipos) em Zig é o processo de converter um valor de um tipo para outro. Zig possui um sistema de coerção cuidadosamente projetado que permite conversões implícitas apenas quando são seguras e sem perda de informação. Para conversões que podem perder dados ou alterar a semântica, Zig exige conversão explícita usando builtins como @intCast, @floatFromInt, @ptrCast, etc.
Essa abordagem elimina uma classe inteira de bugs comuns em C, onde conversões implícitas silenciosas podem truncar valores ou mudar sinais.
Por que Type Coercion Importa
- Segurança: Conversões que podem perder dados exigem declaração explícita do programador.
- Clareza: O código expressa exatamente onde conversões acontecem.
- Previsibilidade: Sem surpresas de promoção implícita como em C (integer promotion rules).
- Portabilidade: O comportamento é o mesmo em todas as arquiteturas.
Exemplo Prático
Coerções Implícitas (Seguras)
const std = @import("std");
pub fn main() void {
// comptime_int para qualquer tipo inteiro que comporte o valor
const a: u32 = 42;
const b: i64 = 42;
// Widening: tipo menor para tipo maior (sem perda)
const c: u16 = 100;
const d: u32 = c; // u16 -> u32 implícito
// Ponteiro para slice
var array = [_]u8{ 1, 2, 3 };
const slice: []u8 = &array; // *[3]u8 -> []u8 implícito
// const qualification: T -> const T
const ptr_mut: *u32 = @constCast(&a);
_ = ptr_mut;
std.debug.print("{} {} {}\n", .{ d, b, slice.len });
}
Coerções Explícitas (Potencialmente Inseguras)
const std = @import("std");
pub fn main() void {
// Narrowing: tipo maior para tipo menor (pode perder dados)
const grande: u32 = 200;
const pequeno: u8 = @intCast(grande); // explícito!
// Float para inteiro (perde parte fracionária)
const pi: f64 = 3.14;
const inteiro: i32 = @intFromFloat(pi); // 3
// Inteiro para float
const n: i32 = 42;
const f: f64 = @floatFromInt(n); // 42.0
std.debug.print("{} {} {d}\n", .{ pequeno, inteiro, f });
}
Coerção com Optionals e Error Unions
const std = @import("std");
fn buscar(id: u32) ?[]const u8 {
if (id == 1) return "encontrado"; // []const u8 -> ?[]const u8 implícito
return null;
}
fn calcular(x: u32) !u32 {
if (x == 0) return error.DivisaoPorZero;
return 100 / x; // u32 -> !u32 implícito
}
pub fn main() void {
const resultado = buscar(1);
if (resultado) |valor| {
std.debug.print("{s}\n", .{valor});
}
const calc = calcular(5) catch 0;
std.debug.print("{}\n", .{calc}); // 20
}
Regras de Coerção Implícita
| De | Para | Permitido |
|---|---|---|
comptime_int | Qualquer inteiro que comporte | Sim |
comptime_float | f32, f64 | Sim |
u8 | u16, u32, u64 | Sim (widening) |
u32 | u16, u8 | Apenas explícito (@intCast) |
*[N]T | []T | Sim |
T | ?T | Sim |
T | E!T | Sim |
*T | *const T | Sim |
Armadilhas Comuns
- Comparar tipos diferentes:
==entreu32ei32não é permitido. Converta explicitamente primeiro. - Aritmética mista: Somar
u8comu32exige que ambos estejam no mesmo tipo. Use@asou@intCast. - Literais negativos em unsigned:
const x: u32 = -1;é erro de compilação. Zig não permite conversão implícita de valor negativo para unsigned. - Perda de precisão float: Converter
f64paraf32exige@floatCast, pois pode perder precisão.
Termos Relacionados
- comptime_int — Tipo que pode ser coagido para qualquer inteiro
- comptime_float — Tipo que pode ser coagido para qualquer float
- anytype — Inferência de tipo para parâmetros genéricos
- Pointer Types — Coerção entre tipos de ponteiro