Return Local Pointer — Como Resolver em Zig

Return Local Pointer — Como Resolver em Zig

O Que Este Erro Significa

O erro de retornar ponteiro para variável local ocorre quando uma função tenta retornar um ponteiro ou slice que referencia dados na stack da função. Quando a função retorna, sua stack frame é destruída e a memória é reutilizada. O ponteiro retornado se torna um “dangling pointer” — aponta para memória inválida que será sobrescrita pela próxima chamada de função.

Zig detecta este erro em tempo de compilação na maioria dos casos:

error: pointer to local variable 'buffer' returned from function

Ou:

error: function returns address of local variable

Este é um erro clássico em linguagens com gerenciamento manual de memória (C, C++), e Zig o previne staticamente quando possível.

Causas Comuns

1. Retornar Ponteiro para Array Local

fn criarBuffer() *[100]u8 {
    var buffer: [100]u8 = undefined;
    @memset(&buffer, 0);
    return &buffer; // ERRO: buffer está na stack e será destruído
}

2. Retornar Slice de Array Local

fn obterDados() []u8 {
    var dados: [50]u8 = undefined;
    @memset(&dados, 'A');
    return &dados; // ERRO: dados está na stack
}

3. Retornar Ponteiro para Struct Local

const Config = struct {
    porta: u16,
    host: []const u8,
};

fn criarConfig() *Config {
    var config = Config{
        .porta = 8080,
        .host = "localhost",
    };
    return &config; // ERRO: config está na stack
}

4. Retornar Ponteiro para Variável de Loop

fn encontrar(arr: []const u32, alvo: u32) ?*const u32 {
    for (arr) |item| {
        if (item == alvo) {
            return &item; // ERRO: item é cópia na stack
        }
    }
    return null;
}

5. Retornar String Formatada em Buffer Local

const std = @import("std");

fn formatarNumero(n: u32) []const u8 {
    var buf: [20]u8 = undefined;
    const resultado = std.fmt.bufPrint(&buf, "{d}", .{n}) catch return "";
    return resultado; // ERRO: buf está na stack
}

Como Corrigir

Solucao 1: Retornar por Valor (Cópia)

A solução mais simples — retorne a struct ou array por valor, não por ponteiro:

const Config = struct {
    porta: u16,
    host: []const u8,
};

fn criarConfig() Config {
    return .{
        .porta = 8080,
        .host = "localhost",
    };
}

pub fn main() void {
    const config = criarConfig(); // Cópia na stack de main
    _ = config;
}

Solucao 2: Alocar no Heap

const std = @import("std");

fn criarBuffer(allocator: std.mem.Allocator) ![]u8 {
    const buffer = try allocator.alloc(u8, 100);
    @memset(buffer, 0);
    return buffer; // OK: memória está no heap
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const buf = try criarBuffer(allocator);
    defer allocator.free(buf);

    buf[0] = 42;
}

Solucao 3: Receber Buffer como Parâmetro

Padrão idiomático em Zig — quem chama fornece o buffer:

const std = @import("std");

fn formatarNumero(n: u32, buf: []u8) ![]u8 {
    return std.fmt.bufPrint(buf, "{d}", .{n});
}

pub fn main() !void {
    var buf: [20]u8 = undefined;
    const resultado = try formatarNumero(42, &buf);
    std.debug.print("Formatado: {s}\n", .{resultado});
    // buf está na stack de main — válido durante todo o escopo
}

Solucao 4: Usar Ponteiro para o Array Original

fn encontrar(arr: []const u32, alvo: u32) ?*const u32 {
    for (arr, 0..) |item, i| {
        if (item == alvo) {
            return &arr[i]; // OK: aponta para o array original, não para cópia
        }
    }
    return null;
}

pub fn main() void {
    const dados = [_]u32{ 10, 20, 30 };
    if (encontrar(&dados, 20)) |ptr| {
        _ = ptr.*;
    }
}

Solucao 5: Retornar String Literal (Comptime)

Strings literais são alocadas no segmento de dados do programa, não na stack:

fn obterMensagem(codigo: u32) []const u8 {
    return switch (codigo) {
        0 => "Sucesso",            // OK: string literal está no binário
        1 => "Erro de entrada",    // OK
        2 => "Erro de conexão",    // OK
        else => "Erro desconhecido", // OK
    };
}

pub fn main() void {
    const msg = obterMensagem(1);
    _ = msg; // Válido — string literal vive para sempre
}

Solucao 6: Usar allocator.create para Structs no Heap

const std = @import("std");

const Config = struct {
    porta: u16,
    host: []const u8,
};

fn criarConfig(allocator: std.mem.Allocator) !*Config {
    const config = try allocator.create(Config);
    config.* = .{
        .porta = 8080,
        .host = "localhost",
    };
    return config; // OK: no heap
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const config = try criarConfig(allocator);
    defer allocator.destroy(config);

    std.debug.print("Porta: {}\n", .{config.porta});
}

Quando É Seguro Retornar Ponteiro

// SEGURO: ponteiro para parâmetro (vida útil >= chamador)
fn primeiro(slice: []u32) *u32 {
    return &slice[0]; // OK: slice existe no chamador
}

// SEGURO: ponteiro para global
var global: u32 = 0;
fn obterGlobal() *u32 {
    return &global; // OK: variável global vive para sempre
}

// SEGURO: ponteiro para memória alocada no heap
fn alocar(allocator: std.mem.Allocator) !*u32 {
    const ptr = try allocator.create(u32);
    ptr.* = 42;
    return ptr; // OK: heap outlives a função
}

Padrões Idiomáticos em Zig

SituaçãoSolução Recomendada
Retornar struct pequenaRetornar por valor
Retornar string formatadaReceber buffer como parâmetro
Retornar dados grandesAlocar no heap (receber allocator)
Retornar texto fixoUsar string literal
Retornar elemento de coleçãoRetornar ponteiro para o original

Comparação com C

// C — PERIGO: compila sem aviso, mas é bug
char* criar_mensagem() {
    char buf[100];
    sprintf(buf, "Olá");
    return buf; // Dangling pointer! Nenhum aviso.
}
// Zig — SEGURO: erro de compilação
fn criarMensagem() []u8 {
    var buf: [100]u8 = undefined;
    return &buf; // ERRO DE COMPILAÇÃO: ponteiro para local
}

Zig previne este bug antes de executar o programa.

Erros Relacionados

Continue aprendendo Zig

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