---
title: "Once, OnceLock e LazyLock em Rust"
url: "https://rustlang.com.br/stdlib/once-oncelock/"
markdown_url: "https://rustlang.com.br/stdlib/once-oncelock.MD"
description: "Guia completo de Once, OnceLock e LazyLock em Rust: inicialização global thread-safe, lazy statics e substituição do lazy_static em português."
date: "2026-02-23"
author: "Equipe Rust Brasil"
---

# Once, OnceLock e LazyLock em Rust

Guia completo de Once, OnceLock e LazyLock em Rust: inicialização global thread-safe, lazy statics e substituição do lazy_static em português.


## O que faz e quando usar

Rust oferece três primitivas na biblioteca padrão para **inicialização única e thread-safe**: `Once`, `OnceLock<T>` e `LazyLock<T>`. Todas garantem que um bloco de código seja executado **exatamente uma vez**, mesmo quando múltiplas threads tentam simultaneamente.

| Tipo | Estável desde | Uso principal |
|---|---|---|
| `Once` | Rust 1.0 | Executar código uma vez (sem armazenar valor) |
| `OnceLock<T>` | Rust 1.70 | Inicializar e armazenar um valor uma vez |
| `LazyLock<T>` | Rust 1.80 | Valor lazy com closure de inicialização |

Use essas primitivas quando:

- Precisa de um **singleton** ou variável global inicializada uma vez.
- Quer substituir a crate `lazy_static` por funcionalidade da biblioteca padrão.
- Precisa de **configuração global** lida de arquivo ou variável de ambiente.
- Quer inicializar pools de conexão, caches ou loggers na primeira chamada.

---

## Tipos e Funções Principais

### Once

| Item | Descrição |
|---|---|
| `Once::new()` | Cria uma nova instância `Once` |
| `once.call_once(f)` | Executa `f` exatamente uma vez; bloqueia threads concorrentes |
| `once.is_completed()` | Retorna `true` se `call_once` já foi executado com sucesso |

### OnceLock<T>

| Item | Descrição |
|---|---|
| `OnceLock::new()` | Cria um `OnceLock` vazio |
| `lock.get()` | Retorna `Option<&T>` — `None` se ainda não foi inicializado |
| `lock.get_or_init(f)` | Retorna `&T`, inicializando com `f` se necessário |
| `lock.set(value)` | Define o valor; retorna `Err(value)` se já foi definido |
| `lock.get_mut()` | Retorna `Option<&mut T>` (requer `&mut self`) |

### LazyLock<T>

| Item | Descrição |
|---|---|
| `LazyLock::new(f)` | Cria com closure de inicialização (executada na 1a vez) |
| `*lazy` / `Deref` | Acessa o valor (inicializa na primeira vez) |
| `LazyLock::force(&lazy)` | Força a inicialização sem desreferenciar |

---

## Exemplos de Código

### Once — executar código uma vez

```rust
use std::sync::Once;

static INIT: Once = Once::new();

fn inicializar() {
    INIT.call_once(|| {
        println!("Inicialização executada!");
        // Configurar logger, abrir conexão, etc.
    });
}

fn main() {
    // Todas chamam, mas só a primeira executa a closure
    inicializar(); // imprime "Inicialização executada!"
    inicializar(); // não imprime nada
    inicializar(); // não imprime nada

    println!("Já foi inicializado? {}", INIT.is_completed());
}
```

### Once com múltiplas threads

```rust
use std::sync::Once;
use std::thread;

static INIT: Once = Once::new();

fn main() {
    let mut handles = vec![];

    for id in 0..5 {
        handles.push(thread::spawn(move || {
            println!("Thread {} tentando inicializar...", id);
            INIT.call_once(|| {
                println!("==> Thread {} executou a inicialização!", id);
                // Simular trabalho de inicialização
                thread::sleep(std::time::Duration::from_millis(100));
            });
            println!("Thread {} continuando após inicialização.", id);
        }));
    }

    for h in handles {
        h.join().unwrap();
    }
    // Apenas UMA thread terá executado a closure
}
```

### OnceLock — armazenar valor inicializado

```rust
use std::sync::OnceLock;

// Variável global que será inicializada uma vez
static CONFIG: OnceLock<Config> = OnceLock::new();

struct Config {
    db_url: String,
    max_conexoes: u32,
    modo_debug: bool,
}

fn obter_config() -> &'static Config {
    CONFIG.get_or_init(|| {
        println!("Carregando configuração...");
        Config {
            db_url: std::env::var("DATABASE_URL")
                .unwrap_or_else(|_| "postgres://localhost/mydb".into()),
            max_conexoes: 10,
            modo_debug: cfg!(debug_assertions),
        }
    })
}

fn main() {
    // Primeira chamada inicializa
    let cfg = obter_config();
    println!("DB: {}", cfg.db_url);

    // Chamadas subsequentes retornam o valor já inicializado
    let cfg2 = obter_config();
    println!("Max conexões: {}", cfg2.max_conexoes);
}
```

### OnceLock com set() explícito

```rust
use std::sync::OnceLock;

static LOGGER: OnceLock<String> = OnceLock::new();

fn configurar_logger(nivel: &str) -> Result<(), String> {
    LOGGER.set(nivel.to_string()).map_err(|val| {
        format!("Logger já configurado com: {}", val)
    })
}

fn log(msg: &str) {
    if let Some(nivel) = LOGGER.get() {
        println!("[{}] {}", nivel, msg);
    } else {
        println!("[sem logger] {}", msg);
    }
}

fn main() {
    log("antes da config");  // [sem logger] antes da config

    configurar_logger("INFO").unwrap();
    log("após primeira config"); // [INFO] após primeira config

    // Tentar configurar novamente falha
    match configurar_logger("DEBUG") {
        Ok(()) => println!("Reconfigurado"),
        Err(e) => println!("Erro: {}", e), // Erro: Logger já configurado com: INFO
    }

    log("continua INFO"); // [INFO] continua INFO
}
```

### LazyLock — substituto do lazy_static!

`LazyLock` combina a declaração e a closure de inicialização em um único tipo:

```rust
use std::sync::LazyLock;
use std::collections::HashMap;

// Substituição direta do lazy_static!
static MAPA_CORES: LazyLock<HashMap<&str, &str>> = LazyLock::new(|| {
    println!("Inicializando mapa de cores...");
    let mut m = HashMap::new();
    m.insert("vermelho", "#FF0000");
    m.insert("verde", "#00FF00");
    m.insert("azul", "#0000FF");
    m.insert("amarelo", "#FFFF00");
    m
});

static REGEX_EMAIL: LazyLock<regex::Regex> = LazyLock::new(|| {
    // Compila a regex apenas uma vez
    regex::Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$").unwrap()
});

fn main() {
    // Primeira vez: inicializa o mapa
    println!("Vermelho: {}", MAPA_CORES["vermelho"]);

    // Segunda vez: já inicializado, retorno imediato
    println!("Azul: {}", MAPA_CORES["azul"]);

    // Exemplo com regex (requer crate regex no Cargo.toml)
    // println!("Email válido: {}", REGEX_EMAIL.is_match("user@example.com"));
}
```

### Comparação: antes e depois do LazyLock

```rust
// ANTES (com crate lazy_static):
//
// use lazy_static::lazy_static;
// lazy_static! {
//     static ref DADOS: Vec<i32> = {
//         println!("Inicializando...");
//         vec![1, 2, 3, 4, 5]
//     };
// }

// DEPOIS (biblioteca padrão, sem dependência externa):
use std::sync::LazyLock;

static DADOS: LazyLock<Vec<i32>> = LazyLock::new(|| {
    println!("Inicializando...");
    vec![1, 2, 3, 4, 5]
});

fn main() {
    println!("Antes de acessar DADOS");
    println!("Soma: {}", DADOS.iter().sum::<i32>());
    println!("Soma de novo: {}", DADOS.iter().sum::<i32>());
}
```

### OnceLock em struct (não-global)

`OnceLock` também é útil para inicialização lazy dentro de structs:

```rust
use std::sync::OnceLock;

struct Recurso {
    nome: String,
    // Inicializado sob demanda
    cache: OnceLock<Vec<String>>,
}

impl Recurso {
    fn new(nome: &str) -> Self {
        Recurso {
            nome: nome.to_string(),
            cache: OnceLock::new(),
        }
    }

    fn obter_dados(&self) -> &[String] {
        self.cache.get_or_init(|| {
            println!("Carregando dados de '{}'...", self.nome);
            // Simular busca demorada
            vec![
                format!("{}-item1", self.nome),
                format!("{}-item2", self.nome),
                format!("{}-item3", self.nome),
            ]
        })
    }
}

fn main() {
    let recurso = Recurso::new("banco");

    // Primeira chamada: carrega dados
    println!("Dados: {:?}", recurso.obter_dados());

    // Segunda chamada: retorna do cache
    println!("Dados (cache): {:?}", recurso.obter_dados());
}
```

---

## Padrões Comuns e Anti-padrões

### Padrão: pool de conexões global

```rust
use std::sync::OnceLock;

// Simular um pool de conexões
struct DbPool {
    url: String,
    max_conn: u32,
}

impl DbPool {
    fn new(url: &str, max: u32) -> Self {
        println!("Criando pool para: {}", url);
        DbPool {
            url: url.to_string(),
            max_conn: max,
        }
    }

    fn query(&self, sql: &str) -> String {
        format!("[{}] Executando: {}", self.url, sql)
    }
}

static POOL: OnceLock<DbPool> = OnceLock::new();

fn db() -> &'static DbPool {
    POOL.get_or_init(|| {
        DbPool::new("postgres://localhost/app", 10)
    })
}

fn main() {
    println!("{}", db().query("SELECT 1"));
    println!("{}", db().query("SELECT * FROM users"));
    // Pool criado apenas uma vez
}
```

### Anti-padrão: panic na closure de Once

```rust
use std::sync::Once;

static INIT: Once = Once::new();

fn main() {
    // Se a closure de call_once entra em panic, o Once fica "envenenado"
    let resultado = std::panic::catch_unwind(|| {
        INIT.call_once(|| {
            panic!("Erro na inicialização!");
        });
    });
    println!("Primeiro call_once: {:?}", resultado); // Err(...)

    // Chamadas subsequentes TAMBÉM vão entrar em panic!
    let resultado2 = std::panic::catch_unwind(|| {
        INIT.call_once(|| {
            println!("Esta closure nunca executa");
        });
    });
    println!("Segundo call_once: {:?}", resultado2); // Err(PoisonError)

    // Para evitar: use get_or_init com tratamento de erro
    // ou garanta que a closure nunca entra em panic
}
```

---

## Garantias de Thread Safety

- `Once`, `OnceLock<T>` e `LazyLock<T>` são `Sync` — podem ser usados em `static` compartilhado entre threads.
- `call_once` e `get_or_init` bloqueiam threads concorrentes até a inicialização completar.
- A closure é executada **exatamente uma vez**, mesmo com centenas de threads chamando simultaneamente.
- Se a closure de `Once::call_once` entra em panic, o `Once` fica "envenenado" e chamadas futuras também entram em panic.
- `OnceLock::get_or_init` é seguro contra poison — se a closure falhar, outra thread pode tentar novamente.

---

## Veja Também

- [Mutex e RwLock em Rust](/stdlib/mutex/) — outra forma de sincronização thread-safe
- [Tipos Atômicos](/stdlib/atomics/) — primitivas de baixo nível usadas internamente
- [Send e Sync](/stdlib/send-sync/) — por que essas primitivas são Sync
- [Cell e RefCell](/stdlib/cell-refcell/) — mutabilidade interior para thread única
- [Padrões de Thread Safety](/stdlib/thread-safety-patterns/) — padrões avançados
- Documentação oficial: [`std::sync::OnceLock`](https://doc.rust-lang.org/std/sync/struct.OnceLock.html)
