---
title: "Cheatsheet: Singleton em Zig"
url: "https://ziglang.com.br/padroes/cheatsheet-singleton-em-zig/"
markdown_url: "https://ziglang.com.br/padroes/cheatsheet-singleton-em-zig.MD"
description: "Design pattern Singleton implementado em Zig: instância única global, inicialização lazy, thread safety e alternativas idiomáticas. Guia completo em português."
date: "2026-02-21"
author: "Zig Brasil"
---

# Cheatsheet: Singleton em Zig

Design pattern Singleton implementado em Zig: instância única global, inicialização lazy, thread safety e alternativas idiomáticas. Guia completo em português.


# Singleton em Zig

O padrão Singleton garante que uma classe/struct tenha apenas **uma instância** durante toda a execução do programa, fornecendo um ponto de acesso global a ela. Em Zig, esse padrão é implementado de forma diferente de linguagens OOP tradicionais, utilizando variáveis globais, `std.once` e comptime.

## Quando Usar

- Gerenciador de configuração global
- Pool de conexões com banco de dados
- Sistema de logging centralizado
- Cache global da aplicação
- Acesso a hardware (ex: GPIO em sistemas embarcados)

## Implementação Básica

### Singleton com variável global

```zig
const std = @import("std");

const Logger = struct {
    nivel: NivelLog,
    arquivo: ?std.fs.File,

    const NivelLog = enum { debug, info, warn, err };

    // Instância global única
    var instancia: ?Logger = null;
    var mutex = std.Thread.Mutex{};

    pub fn obterInstancia() *Logger {
        mutex.lock();
        defer mutex.unlock();

        if (instancia == null) {
            instancia = Logger{
                .nivel = .info,
                .arquivo = null,
            };
        }
        return &instancia.?;
    }

    pub fn log(self: *Logger, nivel: NivelLog, mensagem: []const u8) void {
        if (@intFromEnum(nivel) < @intFromEnum(self.nivel)) return;
        std.debug.print("[{s}] {s}\n", .{ @tagName(nivel), mensagem });
    }
};

pub fn main() void {
    const logger = Logger.obterInstancia();
    logger.log(.info, "Aplicação iniciada");
    logger.log(.warn, "Recurso quase esgotado");
    logger.log(.debug, "Esta mensagem não aparece (nível < info)");

    // Em qualquer parte do código, mesma instância
    const mesmo_logger = Logger.obterInstancia();
    mesmo_logger.log(.err, "Erro crítico!");
}
```

### Singleton com std.once (thread-safe garantido)

```zig
const std = @import("std");

const Config = struct {
    porta: u16,
    host: []const u8,
    max_conexoes: u32,

    var instancia: Config = undefined;
    var once = std.once(inicializar);

    fn inicializar() void {
        instancia = Config{
            .porta = 8080,
            .host = "localhost",
            .max_conexoes = 100,
        };
    }

    pub fn obter() *Config {
        once.call(); // executado apenas uma vez, thread-safe
        return &instancia;
    }
};

pub fn main() void {
    const config = Config.obter();
    std.debug.print("Servidor: {s}:{d}\n", .{ config.host, config.porta });

    // Sempre retorna a mesma instância
    const config2 = Config.obter();
    std.debug.print("Max conexões: {d}\n", .{config2.max_conexoes});
}
```

## Singleton Genérico com comptime

```zig
fn Singleton(comptime T: type, comptime initFn: fn () T) type {
    return struct {
        var instancia: ?T = null;
        var mutex = std.Thread.Mutex{};

        pub fn obter() *T {
            mutex.lock();
            defer mutex.unlock();

            if (instancia == null) {
                instancia = initFn();
            }
            return &instancia.?;
        }

        pub fn resetar() void {
            mutex.lock();
            defer mutex.unlock();
            instancia = null;
        }
    };
}

// Uso:
const MeuSingleton = Singleton(MinhaStruct, MinhaStruct.criar);
const inst = MeuSingleton.obter();
```

## Quando Evitar

- **Testes unitários**: Singletons criam estado global que dificulta testes isolados. Prefira [Dependency Injection](/padroes/dependency-injection/) quando testabilidade é prioridade.
- **Acoplamento excessivo**: Se muitas partes do código dependem do singleton, considere passar a dependência explicitamente.
- **Concorrência pesada**: Para dados compartilhados entre muitas threads, considere um [Pool de Objetos](/padroes/pool-objetos/) ou dados por thread.

## Alternativa Idiomática em Zig

Em Zig, muitas vezes é preferível simplesmente passar a dependência como parâmetro, seguindo a filosofia de tornar tudo explícito:

```zig
const Logger = struct {
    nivel: NivelLog,
    // ...
};

fn processarRequisicao(logger: *Logger, dados: []const u8) !void {
    logger.log(.info, "Processando requisição");
    // ...
}
```

## Considerações de Performance

- **`std.once` é a forma mais eficiente**: internamente, `std.once` usa uma operação atômica para verificar se a inicialização já ocorreu. Após a primeira chamada, `once.call()` é apenas uma leitura atômica com memória ordering `acquire` — custo mínimo, sem lock.
- **Mutex na versão manual**: a implementação com `mutex.lock()` em `obterInstancia` toma um lock em *toda* chamada, mesmo após a inicialização. Para alto volume de acessos concorrentes, isso cria contention. Prefira `std.once` que evita o lock após a primeira inicialização.
- **Singleton em sistemas embarcados**: variáveis globais em Zig vão para o segmento `.data` ou `.bss`. Em microcontroladores com RAM limitada, considere singletons com estado mínimo e inicialização via `comptime` quando possível.

## Singleton com Comptime para Configuração Estática

Quando a configuração é conhecida em tempo de compilação, você pode ter um "singleton" sem custo de runtime algum:

```zig
// config.zig
pub const Config = struct {
    porta: u16,
    max_conexoes: u32,
    debug: bool,
};

// Singleton comptime — zero custo em runtime
pub const config: Config = blk: {
    // Em builds de debug, usa configuração de desenvolvimento
    if (@import("builtin").mode == .Debug) {
        break :blk .{
            .porta = 8080,
            .max_conexoes = 10,
            .debug = true,
        };
    } else {
        break :blk .{
            .porta = 443,
            .max_conexoes = 1000,
            .debug = false,
        };
    }
};

// Uso em qualquer lugar:
// const cfg = @import("config.zig").config;
// std.debug.print("Porta: {d}\n", .{cfg.porta});
```

## Erros Comuns

**Singleton que armazena alocações sem gerenciar lifetime**: se o singleton usa um `ArrayList` internamente, alguém precisa chamar `deinit` em algum momento. Em programas de vida longa, um singleton que nunca libera memória é um vazamento. Registre um handler de deinit no `defer` de `main`.

**Testes que deixam o singleton em estado sujo**: testes que modificam o estado do singleton afetam os testes seguintes. Implemente um método `resetar` (como no exemplo genérico) e chame-o no `teardown` de cada teste.

**Race condition na inicialização manual**: o padrão `if (instancia == null) { instancia = ... }` sem mutex pode ser executado simultaneamente por duas threads, criando duas instâncias. Sempre use `mutex.lock()` antes de verificar `null`, ou use `std.once`.

## Perguntas Frequentes

**Singletons são ruins para testes?**
Sim, quando contêm estado mutável. Um singleton de configuração imutável é inofensivo para testes. Um singleton de logger que acumula mensagens entre testes é problemático. A alternativa é usar [Dependency Injection](/padroes/dependency-injection/) — passe a instância como parâmetro em vez de acessá-la globalmente.

**Posso ter um singleton por thread (thread-local)?**
Sim. Use `threadlocal var instancia: ?MeuTipo = null;`. Cada thread terá sua própria instância, sem necessidade de mutex. Útil para buffers temporários e geradores de números aleatórios.

**Qual é a diferença entre `std.once` e um mutex simples para singleton?**
`std.once` garante que a função de inicialização seja executada exatamente uma vez, mesmo com múltiplas threads competindo. Após a inicialização, `once.call()` é uma leitura atômica barata. Um mutex simples toma o lock em cada chamada a `obterInstancia`, mesmo após a inicialização — menos eficiente para casos de alta frequência.

## Veja Também

- [Dependency Injection](/padroes/dependency-injection/) — Alternativa ao Singleton para testabilidade
- [Factory](/padroes/factory/) — Criação controlada de objetos
- [Concorrência](/cheatsheets/concorrencia/) — Thread safety em Zig
- [FAQ Produção](/faq/faq-producao/) — Padrões para código em produção
