Builders con estado de tipos
Codifique la validez en el sistema de tipos
Builders con estado de tipos es una lección gratuita de Learn Rust Coding en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Learn Rust Coding, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Learn Rust Coding incluye 4 lecciones en total.
El problema de los builders simples
Un builder normal permite llamar a build() en cualquier momento, incluso antes de establecer los campos obligatorios. Los datos ausentes se manifiestan entonces como un panic en tiempo de ejecución o como un Err.
Los builders con estado de tipos trasladan esta comprobación al momento de compilación: olvidar un paso obligatorio simplemente impide compilar.
Codificar el estado en los tipos
La idea consiste en hacer que el builder sea genérico respecto a tipos marcador que representen qué pasos se han completado. Al establecer cada campo, cambia el tipo del builder.
El método build() solo está disponible cuando todos los marcadores obligatorios alcanzan el estado «establecido».
struct Missing;
struct Set;Un Builder genérico respecto a marcadores
Proporcione al tipo builder parámetros de tipo para el estado de cada campo obligatorio. PhantomData transporta el marcador sin almacenar datos reales.
use std::marker::PhantomData;
struct ReqBuilder<H, U> {
url: Option<String>,
method: Option<String>,
_state: PhantomData<(H, U)>,
}El estado inicial
El constructor devuelve un builder en el que todos los marcadores obligatorios son Missing. En este punto no existe build(), por lo que el sistema de tipos sabe que el objeto está incompleto.
impl ReqBuilder<Missing, Missing> {
fn new() -> Self {
ReqBuilder { url: None, method: None, _state: PhantomData }
}
}Transicionar un marcador
Un setter consume el builder antiguo y devuelve uno nuevo cuyo marcador para ese campo ha cambiado a Set. El otro marcador se conserva manteniendo genérico su parámetro de tipo.
impl<U> ReqBuilder<Missing, U> {
fn url(self, url: &str) -> ReqBuilder<Set, U> {
ReqBuilder { url: Some(url.to_string()),
method: self.method, _state: PhantomData }
}
}La segunda transición
Establecer el método funciona de la misma manera: cambia el segundo marcador de Missing a Set y deja intacto el primero.
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() solo cuando todo está establecido
Lo esencial es que build() solo se implementa para ReqBuilder<Set, Set>. En cualquier otro estado, ese método no existe, por lo que la llamada no compila.
Dentro de él, las llamadas a unwrap nunca pueden producir un panic porque el tipo demuestra que ambos campos están presentes.
struct Request { url: String, method: String }
impl ReqBuilder<Set, Set> {
fn build(self) -> Request {
Request { url: self.url.unwrap(), method: self.method.unwrap() }
}
}Unir todas las piezas
Una cadena correcta compila sin problemas porque cada llamada acerca el builder a ReqBuilder<Set, Set>, donde existe build().
fn demo() -> Request {
ReqBuilder::new()
.url("https://example.com")
.method("GET")
.build()
}El error de compilación que desea
Si omite un paso obligatorio, el compilador lo rechaza. Llamar a build() sobre ReqBuilder<Set, Missing> informa de que «no existe ningún método llamado build», detectando la omisión antes de que el programa llegue a ejecutarse.
// ReqBuilder::new().url("x").build();
// error: no method named `build` found for
// ReqBuilder<Set, Missing>Una versión ejecutable independiente
Este programa mínimo utiliza un único campo obligatorio para mostrar toda la transición de principio a fin. Compila e imprime el valor construido.
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);
}Costes y compromisos
Los builders con estado de tipos ofrecen garantías en tiempo de compilación sin sobrecoste en tiempo de ejecución, ya que los marcadores se eliminan. El precio es una mayor complejidad del sistema de tipos y más bloques impl a medida que aumentan los campos obligatorios.
Reserve este patrón para las API en las que una construcción incompleta deba ser imposible por diseño.
Comprobación rápida
Identifique qué es lo que realmente garantiza que un builder con estado de tipos esté completo.
Resumen
Los builders con estado de tipos codifican cada paso obligatorio como un parámetro de tipo marcador, transportado por PhantomData. Los setters consumen el builder y devuelven un tipo nuevo cuyo marcador cambia a Set.
Como build() solo se implementa para el estado en el que todos los marcadores son Set, olvidar un paso produce un error de compilación, no un panic en tiempo de ejecución, y todo ello sin coste de ejecución.
Preguntas frecuentes
¿La lección «Builders con estado de tipos» es gratis?
Sí — el texto completo de «Builders con estado de tipos» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Learn Rust Coding, actualiza a CoddyKit PRO. El curso de Learn Rust Coding incluye 4 lecciones en total.
¿Qué aprenderé en «Builders con estado de tipos»?
Codifique la validez en el sistema de tipos Practicas Learn Rust Coding con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Learn Rust Coding?
No se requiere experiencia previa. Learn Rust Coding en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.
¿Cuánto tiempo toma la lección «Builders con estado de tipos»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Learn Rust Coding?
Sí. Cada lección de Learn Rust Coding incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- El patrón Builder
- El patrón Newtype
- Builders con estado de tipos
- Ergonomía de Deref y wrappers