0Pricing
Learn Rust Coding · Aula

Builders com estado de tipo

Codifique a validade no sistema de tipos.

Builders com estado de tipo é uma aula grátis de Learn Rust Coding no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Learn Rust Coding, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Learn Rust Coding inclui 4 aulas no total.

O problema com builders comuns

Um builder comum permite chamar build() a qualquer momento, até mesmo antes de definir os campos obrigatórios. Os dados ausentes aparecem, então, como um pânico em tempo de execução ou como um Err.

Builders com estado de tipo transferem essa verificação para o momento da compilação: esquecer uma etapa obrigatória simplesmente faz a compilação falhar.

Codificando o estado nos tipos

O truque é tornar o builder genérico em relação a tipos marcadores que representam quais etapas foram concluídas. À medida que você define cada campo, o tipo do builder muda.

Somente quando todos os marcadores obrigatórios alcançam o estado “definido” um método build() fica disponível.

struct Missing;
struct Set;

Um builder genérico em relação a marcadores

Dê ao tipo builder parâmetros de tipo para o estado de cada campo obrigatório. PhantomData carrega o marcador sem armazenar dados reais.

use std::marker::PhantomData;
struct ReqBuilder<H, U> {
    url: Option<String>,
    method: Option<String>,
    _state: PhantomData<(H, U)>,
}

O estado inicial

O construtor retorna um builder em que todos os marcadores obrigatórios são Missing. Nesse ponto, não existe build(), portanto o sistema de tipos sabe que o objeto está incompleto.

impl ReqBuilder<Missing, Missing> {
    fn new() -> Self {
        ReqBuilder { url: None, method: None, _state: PhantomData }
    }
}

Fazendo a transição de um marcador

Um método de configuração consome o builder antigo e retorna um novo, com o marcador desse campo alterado para Set. O outro marcador é preservado mantendo seu parâmetro de tipo genérico.

impl<U> ReqBuilder<Missing, U> {
    fn url(self, url: &str) -> ReqBuilder<Set, U> {
        ReqBuilder { url: Some(url.to_string()),
            method: self.method, _state: PhantomData }
    }
}

A segunda transição

Definir o método funciona da mesma forma: o segundo marcador muda de Missing para Set, enquanto o primeiro permanece inalterado.

impl<H> ReqBuilder<H, Missing> {
    fn method(self, m: &str) -> ReqBuilder<H, Set> {
        ReqBuilder { url: self.url,
            method: Some(m.to_string()), _state: PhantomData }
    }
}

build() somente quando tudo está definido

O ponto fundamental é que build() só é implementado para ReqBuilder<Set, Set>. Qualquer outro estado simplesmente não tem esse método, portanto a chamada não compila.

Internamente, as chamadas a unwrap nunca podem causar pânico, pois o tipo prova que ambos os campos estão presentes.

struct Request { url: String, method: String }
impl ReqBuilder<Set, Set> {
    fn build(self) -> Request {
        Request { url: self.url.unwrap(), method: self.method.unwrap() }
    }
}

Juntando tudo

Uma cadeia correta compila sem problemas porque cada chamada move o builder em direção a ReqBuilder<Set, Set>, onde build() existe.

fn demo() -> Request {
    ReqBuilder::new()
        .url("https://example.com")
        .method("GET")
        .build()
}

O erro de compilação que você quer

Pule uma etapa obrigatória e o compilador recusará o código. Chamar build() em ReqBuilder<Set, Missing> informa “nenhum método chamado build”, detectando a omissão antes mesmo de o programa ser executado.

// ReqBuilder::new().url("x").build();
// error: no method named `build` found for
// ReqBuilder<Set, Missing>

Uma versão executável autocontida

Este programa mínimo usa um único campo obrigatório para mostrar toda a transição, do início ao fim. Ele compila e imprime o valor construído.

use std::marker::PhantomData;
struct Missing; struct Set;
struct B<N> { name: Option<String>, _s: PhantomData<N> }
impl B<Missing> {
    fn new() -> Self { B { name: None, _s: PhantomData } }
    fn name(self, n: &str) -> B<Set> {
        B { name: Some(n.to_string()), _s: PhantomData }
    }
}
impl B<Set> {
    fn build(self) -> String { self.name.unwrap() }
}
fn main() {
    let v = B::new().name("prod").build();
    println!("built: {}", v);
}

Custos e compromissos

Builders com estado de tipo oferecem garantias em tempo de compilação sem sobrecarga em tempo de execução, pois os marcadores são removidos. O preço é uma maquinaria de tipos mais complexa e blocos de implementação combinatórios à medida que o número de campos obrigatórios cresce.

Reserve esse padrão para APIs em que uma construção incompleta precise ser impossível por definição.

Verificação rápida

Identifique o que realmente garante a completude em um builder com estado de tipo.

Recapitulação

Os construtores baseados em estados de tipo codificam cada etapa obrigatória como um parâmetro de tipo marcador, transportado por PhantomData. Os métodos configuradores consomem o construtor e retornam um novo tipo com um marcador alterado para Set.

Como build() só é implementado para o estado em que todos os marcadores são Set, esquecer uma etapa causa um erro de compilação, não um pânico em tempo de execução, sem nenhum custo durante a execução.

Perguntas Frequentes

A aula “Builders com estado de tipo” é grátis?

Sim — o texto completo de “Builders com estado de tipo” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Learn Rust Coding, atualize para CoddyKit PRO. O curso de Learn Rust Coding inclui 4 aulas no total.

O que vou aprender em “Builders com estado de tipo”?

Codifique a validade no sistema de tipos. Você pratica Learn Rust Coding com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Learn Rust Coding?

Nenhuma experiência prévia é necessária. Learn Rust Coding no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “Builders com estado de tipo”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Learn Rust Coding?

Sim. Cada aula de Learn Rust Coding inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. O padrão Builder
  2. O padrão Newtype
  3. Builders com estado de tipo
  4. Ergonomia de Deref e wrappers
← Voltar para Learn Rust Coding