@constCast em Zig
O @constCast remove o qualificador const de um ponteiro, transformando um *const T em *T. Esta operação é necessária em cenários de interoperabilidade com C ou quando uma API externa exige ponteiro mutável para dados que você sabe serem seguros de modificar. Use com cautela — modificar dados originalmente const é undefined behavior.
Sintaxe
@constCast(ptr: *const T) *T
Parâmetros
- ptr (
*const T): Ponteiro const a ser convertido para mutável.
Valor de retorno
Retorna *T — o mesmo ponteiro, agora sem a restrição de const.
Exemplos práticos
Exemplo 1: Interop com API C que exige ponteiro mutável
const std = @import("std");
// Simulação de uma função C que recebe ponteiro mutável
// mas não modifica os dados (API mal projetada)
fn funcaoC(dados: [*]u8, tamanho: usize) void {
// Apenas lê os dados
var soma: u64 = 0;
for (dados[0..tamanho]) |b| {
soma += b;
}
std.debug.print("Soma: {}\n", .{soma});
}
pub fn main() void {
const mensagem = "Hello, Zig!";
// A função C exige [*]u8, mas temos []const u8
funcaoC(@constCast(mensagem.ptr), mensagem.len);
}
Exemplo 2: Adaptador entre APIs const e mutáveis
const std = @import("std");
fn processarMutavel(dados: []u8) void {
// Processa sem modificar
for (dados) |byte| {
std.debug.print("{c}", .{byte});
}
std.debug.print("\n", .{});
}
fn adaptador(dados: []const u8) void {
// Sabemos que processarMutavel não modifica os dados
processarMutavel(@constCast(dados));
}
pub fn main() void {
const texto: []const u8 = "Zig Brasil";
adaptador(texto);
}
Exemplo 3: Cast de slice const
const std = @import("std");
pub fn main() void {
const dados: []const u8 = "imutável";
// Remover const do slice inteiro
const mutavel: []u8 = @constCast(dados);
_ = mutavel;
// CUIDADO: modificar dados originalmente const é UB!
// mutavel[0] = 'I'; // Undefined behavior!
}
Casos de uso comuns
- Interop com C: APIs C que recebem
char*em vez deconst char*mesmo sem modificar. - Callbacks genéricos: Quando uma interface exige ponteiro mutável mas a implementação não modifica.
- Testes: Simular condições onde constness precisa ser contornada.
Regras importantes
- Nunca modifique dados originalmente const: Se os dados foram declarados como
const, modificá-los após@constCasté undefined behavior. - Use
@constCastapenas quando tiver certeza de que a mutabilidade é segura. - Prefira redesenhar a API para aceitar
*const Tem vez de usar@constCast.
Por que @constCast é raramente necessário
Em código Zig puro, @constCast quase nunca é necessário. O sistema de tipos do Zig é projetado para que const seja propagado corretamente. A necessidade de @constCast geralmente indica um de dois cenários:
- API C mal projetada: A função C deveria declarar o parâmetro como
const char*mas não declara, por descuido ou compatibilidade histórica. - Design de API a ser melhorado: Se você está escrevendo Zig e precisa de
@constCast, considere redesenhar a API para aceitar o tipo correto.
Equivalente em C
Em C, const_cast<T*> existe apenas em C++. Em C puro, você usa um cast explícito:
const char *str = "hello";
char *mutable_str = (char *)str; // C puro — sem aviso
O Zig é mais rigoroso: sem @constCast, o compilador rejeita a conversão de *const T para *T, forçando o programador a ser explícito sobre a intenção.
Quando @constCast é seguro
@constCast é seguro quando:
- Os dados foram originalmente declarados sem
conste você está apenas adaptando uma interface - A função receptora não modificará os dados (você garante isso ao ler o código)
- O dado está em memória de leitura/escrita (não em seção
.rodata)
// SEGURO: dados originalmente mutáveis
var buffer = [_]u8{ 1, 2, 3 };
const slice: []const u8 = &buffer; // const view de dados mutáveis
const mutavel: []u8 = @constCast(slice); // ok, original é mutável
// PERIGOSO: string literal fica em .rodata
const str = "literal";
const mut: []u8 = @constCast(str); // @constCast compila, mas...
mut[0] = 'L'; // Undefined behavior! String literal é somente leitura
Verificação em modo safe
Em modo Debug e ReleaseSafe, o Zig não tem verificação automática para uso incorreto de @constCast — diferente de @alignCast. A responsabilidade de não modificar dados originalmente const é inteiramente do programador.
Ferramentas como AddressSanitizer podem detectar escritas em memória somente leitura em runtime.
Erros comuns
Usar @constCast em string literals: String literals em Zig ficam na seção .rodata do binário, que é somente leitura. Modificar uma string literal após @constCast causa undefined behavior — tipicamente um segfault ou comportamento silenciosamente incorreto.
Propagar @constCast desnecessariamente: Se você precisa de @constCast em muitos lugares, o problema provavelmente está no design da API. Revise se as funções intermediárias devem aceitar []const u8 ou []u8.
Perguntas Frequentes
@constCast funciona com slices?
Sim. @constCast([]const u8) retorna []u8. O Zig aplica o cast tanto ao ponteiro quanto ao tipo do slice.
Existe @volatileCast equivalente?
Sim, existe @volatileCast para remover o qualificador volatile de ponteiros. O uso é análogo ao @constCast.
Por que o compilador Zig não avisa quando uso @constCast de forma perigosa?
O compilador não tem como saber em tempo de compilação se os dados originalmente eram constantes em memória somente leitura ou mutáveis com view const. Essa distinção existe apenas em runtime. Por isso, a responsabilidade é do programador.
Builtins relacionados
- @ptrCast — Conversão entre tipos de ponteiro
- @alignCast — Conversão de alinhamento
- @volatileCast — Remover qualificador volatile
- @as — Conversão segura de tipos