@as em Zig
O @as é o builtin de conversão explícita de tipos (type cast) do Zig. Ele permite converter um valor de um tipo para outro de forma segura e explícita, garantindo que a conversão é válida. Ao contrário de casts inseguros em C, o @as só permite conversões que o compilador pode verificar como seguras ou que resultam em coerção implícita documentada.
Sintaxe
@as(comptime T: type, valor: anytype) T
O que faz
O @as realiza uma conversão de tipo explícita (type coercion), convertendo o valor fornecido para o tipo de destino especificado. O Zig possui um sistema sofisticado de coerção implícita, e o @as é a forma de invocar esse sistema explicitamente.
Essa conversão é usada quando o compilador não consegue inferir o tipo desejado automaticamente, ou quando o programador deseja tornar a intenção clara no código.
Parâmetros
- T (
type, comptime): O tipo de destino para a conversão. Deve ser conhecido em tempo de compilação. - valor (
anytype): O valor a ser convertido. Deve ser compatível com o tipo de destino segundo as regras de coerção do Zig.
Valor de retorno
Retorna o valor convertido para o tipo T.
Exemplos práticos
Exemplo 1: Removendo ambiguidade de tipos literais
const std = @import("std");
test "tipo literal com @as" {
// Literais inteiros são comptime_int — @as define o tipo concreto
const x = @as(u32, 42);
try std.testing.expect(@TypeOf(x) == u32);
// Sem @as, o compilador pode não saber qual tipo usar
const y = @as(i64, -100);
try std.testing.expect(@TypeOf(y) == i64);
// Literais float são comptime_float
const pi = @as(f32, 3.14159);
try std.testing.expect(@TypeOf(pi) == f32);
}
Exemplo 2: Conversão em chamadas de função
const std = @import("std");
fn processarBytes(dados: []const u8) usize {
return dados.len;
}
fn somarF64(a: f64, b: f64) f64 {
return a + b;
}
test "conversão para argumentos de função" {
// Converter inteiros para o tipo esperado pela função
const resultado = somarF64(@as(f64, 10), @as(f64, 20));
try std.testing.expect(resultado == 30.0);
// Converter string literal para slice
const tamanho = processarBytes(@as([]const u8, "hello"));
try std.testing.expect(tamanho == 5);
}
Exemplo 3: Uso com optionals e error unions
const std = @import("std");
fn buscar(id: u32) ?[]const u8 {
if (id == 1) return "encontrado";
return null;
}
test "conversão com optionals" {
// Converter null para um tipo optional específico
const valor: ?u32 = @as(?u32, null);
try std.testing.expect(valor == null);
// Converter um valor para optional
const numero: ?u32 = @as(?u32, 42);
try std.testing.expect(numero.? == 42);
// Converter para error union
const resultado: anyerror!u32 = @as(anyerror!u32, 100);
try std.testing.expect((try resultado) == 100);
}
Casos de uso comuns
Desambiguação de literais: Quando um literal numérico pode ser interpretado como vários tipos,
@asdefine o tipo exato.Parâmetros de formatação: Em
std.debug.printe funções similares,@asgarante que os valores no tuple.{}tenham o tipo correto.Conversão segura entre tipos numéricos: Converter entre tamanhos de inteiros ou entre inteiro e float quando a conversão é segura.
Inicialização de optionals e error unions: Converter
nullou valores para tipos optional/error union específicos.Interoperabilidade: Converter tipos ao chamar funções C importadas que esperam tipos específicos.
const c = @cImport(@cInclude("stdlib.h"));
fn alocar(tamanho: usize) ?[*]u8 {
return @as(?[*]u8, @ptrCast(c.malloc(tamanho)));
}
Diferença entre @as e outros casts
O @as realiza apenas coerções seguras que o Zig já permitiria implicitamente. Para conversões que alteram a representação ou a interpretação dos dados, existem builtins específicos:
@intFromFloat/@floatFromInt— Conversão entre inteiro e float@ptrCast— Conversão entre tipos de ponteiro@intFromPtr/@ptrFromInt— Conversão entre ponteiro e inteiro@truncate— Truncar inteiros para tamanhos menores
Perguntas Frequentes
Quando usar @as vs conversão implícita?
O Zig permite coerção implícita em muitos casos — por exemplo, um u8 é automaticamente promovido para u32 quando necessário. Use @as quando o compilador não consegue inferir o tipo, quando você quer tornar a intenção explícita no código, ou quando está trabalhando com literais comptime que poderiam ser ambíguos.
@as pode causar perda de dados?
Não. @as só permite coerções que o Zig considera seguras — por exemplo, de um tipo menor para um maior (u8 → u32), ou de comptime_int para um tipo concreto. Para conversões que podem perder dados (como u32 → u8), use @truncate ou @intCast. O compilador emitirá um erro se você tentar usar @as para uma conversão não permitida.
Por que o erro “expected type X, found Y” some com @as?
Literais como 42 têm tipo comptime_int e literais como 3.14 têm tipo comptime_float. Esses tipos se adaptam ao contexto, mas às vezes o compilador não tem contexto suficiente. @as(u32, 42) força o tipo explicitamente, resolvendo a ambiguidade.
Coerções permitidas pelo @as
O @as segue as mesmas regras de coerção implícita do Zig. As principais são:
| De | Para | Observação |
|---|---|---|
comptime_int | qualquer inteiro | sem perda se valor cabe |
comptime_float | qualquer float | sem perda |
| inteiro menor | inteiro maior (mesmo sinal) | zero-extend ou sign-extend |
T | ?T | wrap em optional |
T | E!T | wrap em error union |
*[N]T | []T | coerção de array para slice |
[*:0]T | [*]T | remover sentinela |
Qualquer conversão fora dessas regras exige builtins específicos como @intCast, @floatCast, @ptrCast, etc.
Builtins relacionados
- @intFromFloat — Converte float para inteiro
- @floatFromInt — Converte inteiro para float
- @ptrCast — Conversão entre tipos de ponteiro
- @intFromPtr — Converte ponteiro para inteiro
- @TypeOf — Obtém o tipo de uma expressão