anytype em Zig permite inferir o tipo de um parâmetro a partir do argumento da chamada, como em fn dobrar(x: anytype) @TypeOf(x). O tipo é conhecido em tempo de compilação, mas o valor pode chegar em runtime. Não é tipagem dinâmica nem o aplicativo de notas Anytype: é uma forma de escrever funções genéricas sem <T>.
Use anytype quando a função só precisa aceitar o valor passado e operar sobre ele — writers, helpers matemáticos, formatadores. Se vários parâmetros precisam do mesmo tipo nomeado, prefira comptime T: type. Referência rápida: cheatsheet de comptime.
Resumo rápido
| Pergunta | Resposta curta |
|---|---|
O que é anytype? | Parâmetro genérico com tipo inferido na chamada |
| É o app Anytype? | Não — aqui é a keyword da linguagem Zig |
| Tipo ou valor em comptime? | O tipo é conhecido em comptime; o valor pode ser de runtime |
| Como ler o tipo? | @TypeOf(param) e @typeInfo |
| Alternativa explícita | comptime T: type |
| Uso clássico | Writers, print/log, helpers genéricos |
| Parecido com | Templates C++ / generics Rust, sem <T> |
Definição
anytype é uma anotação de tipo especial em Zig que permite que o compilador infira o tipo de um parâmetro a partir do argumento passado na chamada da função. É o mecanismo principal para criar funções genéricas em Zig sem precisar de uma sintaxe separada de templates.
Quando uma função aceita anytype, o compilador gera uma versão especializada para cada combinação de tipos usada. Isso se aproxima de templates em C++, com sintaxe mais enxuta e erros que normalmente apontam para o uso concreto do valor.
anytype em Zig ≠ app Anytype
Se a busca por anytype trouxe você até aqui por causa do aplicativo de notas Anytype, este verbete trata de outra coisa: a keyword da linguagem Zig. O app Anytype não entra na sintaxe, no compilador nem na biblioteca padrão do Zig. Daqui em diante, anytype significa apenas o parâmetro genérico com tipo inferido.
Por que anytype importa
- Genéricos simples: cria funções que aceitam vários tipos sem boilerplate de
<T>. - Duck typing em comptime: o compilador verifica se o tipo realmente oferece as operações usadas.
- Zero overhead: cada especialização é compilada individualmente, sem vtable obrigatória.
- Composição: combina com
@TypeOfe@typeInfopara introspecção e validação.
Exemplos práticos de anytype
Os quatro programas completos abaixo foram compilados e executados com Zig 0.15.2. Salve cada um separadamente como anytype.zig e execute zig run anytype.zig. A interface de I/O varia entre versões; o exemplo de writer usa std.Io.Writer, não o antigo std.io.fixedBufferStream.
Função genérica com anytype
const std = @import("std");
fn dobrar(valor: anytype) @TypeOf(valor) {
return valor * 2;
}
pub fn main() void {
const a: u32 = dobrar(@as(u32, 21));
const b: f64 = dobrar(@as(f64, 1.5));
const c: i8 = dobrar(@as(i8, -10));
std.debug.print("u32: {}\n", .{a}); // 42
std.debug.print("f64: {d}\n", .{b}); // 3.0
std.debug.print("i8: {}\n", .{c}); // -20
}
anytype com introspecção de tipo
const std = @import("std");
fn imprimir(valor: anytype) void {
const T = @TypeOf(valor);
const info = @typeInfo(T);
switch (info) {
.int, .comptime_int => std.debug.print("Inteiro: {}\n", .{valor}),
.float, .comptime_float => std.debug.print("Float: {d}\n", .{valor}),
.pointer => |ptr_info| {
if (ptr_info.size == .slice and ptr_info.child == u8) {
std.debug.print("String: {s}\n", .{valor});
} else {
std.debug.print("Ponteiro: {*}\n", .{valor});
}
},
.bool => std.debug.print("Bool: {}\n", .{valor}),
else => std.debug.print("Tipo: {s}\n", .{@typeName(T)}),
}
}
pub fn main() void {
imprimir(@as(u32, 42));
imprimir(@as(f64, 3.14));
imprimir(true);
imprimir(@as([]const u8, "Zig"));
}
O cast para []const u8 faz a string cair no ramo .slice. Sem o cast, o literal "Zig" tem tipo *const [3:0]u8 (ponteiro para array com sentinela), e não um slice.
Writer genérico com anytype
const std = @import("std");
fn escreverSaudacao(writer: anytype, nome: []const u8) !void {
try writer.print("Olá, {s}! Bem-vindo ao Zig.\n", .{nome});
}
pub fn main() !void {
var buffer: [128]u8 = undefined;
var writer = std.Io.Writer.fixed(&buffer);
try escreverSaudacao(&writer, "Brasil");
std.debug.print("{s}", .{writer.buffered()});
}
Aqui, anytype exige apenas que o argumento permita a chamada writer.print(...); isso é verificado quando a função é instanciada. Se a API aceita somente a interface std.Io.Writer, declarar writer: *std.Io.Writer torna o contrato explícito e dispensa esse genérico. Use anytype quando realmente quiser aceitar diferentes tipos compatíveis. Veja também o guia de migração de writers.
anytype vs comptime T: type
| Aspecto | anytype | comptime T: type |
|---|---|---|
| Sintaxe | Implícita | Explícita |
| Acesso ao tipo | Via @TypeOf(param) | Diretamente via T |
| Vários parâmetros do mesmo tipo | Dois anytype são independentes; b: @TypeOf(a) relaciona os tipos | a: T, b: T exige argumentos compatíveis com T |
| Uso típico | Funções curtas, writers, helpers | Contêineres e APIs com tipo nomeado |
Regra prática: comece com anytype quando a assinatura fica mais clara sem nomear o tipo; mude para comptime T: type quando o tipo precisa ser reutilizado, documentado ou compartilhado entre campos e parâmetros.
Estas três assinaturas deixam a diferença concreta:
fn independente(a: anytype, b: anytype) void { // tipos independentes
_ = a;
_ = b;
}
fn inferido(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
return a + b;
}
fn explicito(comptime T: type, a: T, b: T) T {
return a + b;
}
Por exemplo, inferido(@as(u32, 10), 20) e explicito(u32, 10, 20) retornam 30. O literal 20 pode ser convertido para u32; não é necessário que todo argumento tenha originalmente esse tipo.
anytype não exige um valor comptime
valor: anytype infere o tipo; comptime valor: anytype exige também que o valor seja conhecido na compilação. Esta função aceita normalmente uma variável modificada em runtime:
const std = @import("std");
fn dobrar(valor: anytype) @TypeOf(valor) {
return valor * 2;
}
test "anytype aceita valor de runtime" {
var numero: u32 = 20;
numero += 1;
try std.testing.expectEqual(@as(u32, 42), dobrar(numero));
}
Salve esse teste em anytype-teste.zig e rode zig test anytype-teste.zig. @TypeOf(numero) é u32, mesmo que o valor de numero não seja uma constante de compilação.
Cuidado com literais numéricos
Em dobrar(21), o parâmetro é inferido como comptime_int, não como u32. Se o corpo depender de dados de runtime, esse tipo pode causar um erro como unable to resolve comptime value. Para escolher um inteiro de runtime, passe dobrar(@as(u32, 21)) ou uma variável tipada. Para floats, o equivalente é @as(f64, 1.5).
Validando tipos com @compileError
Quando anytype recebe um tipo inadequado, o erro padrão pode apontar para uma linha interna pouco amigável. Valide cedo:
const std = @import("std");
fn somar(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
const T = @TypeOf(a);
const info = @typeInfo(T);
if (info != .int and info != .float and info != .comptime_int and info != .comptime_float) {
@compileError("somar() requer tipo numérico, recebeu: " ++ @typeName(T));
}
return a + b;
}
pub fn main() void {
std.debug.print("{}\n", .{somar(@as(u32, 10), @as(u32, 20))});
}
Neste exemplo, b: @TypeOf(a) garante que o segundo argumento seja compatível com o tipo do primeiro. A validação cobre o tipo numérico, não overflow: u32 continua tendo os limites de u32. Para verificar a mensagem de erro, substitua a chamada por somar(true, false); a compilação deve falhar com somar() requer tipo numérico, recebeu: bool.
Casos de uso típicos
- Funções matemáticas:
fn abs(x: anytype) @TypeOf(x)para tipos numéricos. - Writers genéricos: funções que aceitam qualquer valor com
.print/.writeAll. - Logging e formatação: helpers que formatam valores arbitrários em comptime.
- Wrappers: decorators que envolvem outro valor sem conhecer o tipo concreto.
Para o mecanismo mais amplo por trás disso, veja o guia de comptime em Zig e a página de reflexão com comptime.
Armadilhas comuns
- Erros difíceis de ler: valide com
@typeInfoe@compileErrorno início da função. - Dois
anytypediferentes: dois parâmetrosanytypepodem receber tipos distintos; useb: @TypeOf(a)oucomptime T: typecoma: T, b: Tpara relacioná-los. - Não é um tipo para variáveis ou campos:
var x: anytypeestruct { x: anytype }não são declarações válidas. Use um tipo concreto ou uma função que retorna um tipo genérico. - Não é dinâmico:
anytypenão cria um objeto “qualquer” em runtime. - Tipo de retorno: use
@TypeOf(param)quando o retorno deve acompanhar o argumento. - APIs de I/O em evolução: em Zig 0.16, writers e
std.Iomudaram; ao copiar exemplos antigos comgetStdOut().writer(), confira o guia de migração Writergate e as novidades do Zig 0.16.
Perguntas frequentes
O que é anytype em Zig?
anytype é o parâmetro cujo tipo o compilador infere na chamada. É a forma mais direta de escrever genéricos implícitos em Zig.
anytype em Zig é o mesmo que o app Anytype?
Não. Aqui anytype é uma palavra-chave da linguagem Zig. O aplicativo Anytype, de notas e bases de conhecimento, é um produto separado e não faz parte da sintaxe do Zig.
anytype é tipagem dinâmica?
Não. O tipo é resolvido estaticamente e não há boxing nem dispatch dinâmico implícito. Isso não exige que o valor seja conhecido em comptime: uma variável u32 de runtime também pode ser passada para anytype.
Quando preferir comptime T: type?
Quando o tipo precisa de nome, aparece em vários parâmetros/campos ou faz parte da API pública de um contêiner genérico.
Como inspecionar o tipo inferido?
Use @TypeOf(valor) para obter o tipo e @typeInfo para ler a tagged union descritiva desse tipo. Veja também o builtin @typeInfo.
Por que o erro de anytype às vezes é confuso?
Porque a falha aparece no ponto em que a operação incompatível é usada, não necessariamente na assinatura. Valide cedo com @typeInfo + @compileError (veja a seção acima) para mensagens acionáveis.
anytype em Zig é igual a template C++ ou generic Rust?
Conceitualmente sim: o compilador gera uma especialização por tipo. A diferença prática é a sintaxe e o duck typing em comptime — em Zig você escreve anytype (ou comptime T: type) e o corpo da função define quais operações o tipo precisa oferecer.
Termos relacionados
- Struct — tipos compostos e genéricos com
comptime - Tagged Union — unions discriminadas usadas em introspecção
- Sentinel — slices e arrays terminados por sentinela
- usize — inteiro de tamanho nativo para índices e tamanhos
Tutoriais relacionados
- Comptime em Zig — Guia Completo
- Zig Comptime e Reflection
- Cheatsheet de comptime
- Writers em Zig e o padrão anytype
- Array de Strings em Zig
Referência oficial
- Zig 0.15.2: inferência de tipos de parâmetros — definição de
anytype,@TypeOfe exemplo de teste.