@constCast em Zig — Referência e Exemplos

@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

  1. Interop com C: APIs C que recebem char* em vez de const char* mesmo sem modificar.
  2. Callbacks genéricos: Quando uma interface exige ponteiro mutável mas a implementação não modifica.
  3. 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 @constCast apenas quando tiver certeza de que a mutabilidade é segura.
  • Prefira redesenhar a API para aceitar *const T em 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:

  1. 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.
  2. 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:

  1. Os dados foram originalmente declarados sem const e você está apenas adaptando uma interface
  2. A função receptora não modificará os dados (você garante isso ao ler o código)
  3. 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

Tutoriais relacionados

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.