El patrón "Parse, don't validate" aplicado a Rust: cómo los tipos eliminan comprobaciones redundantes

Rusty thoughts on "Parse, don't validate"

El artículo de Alexis King "Parse, don't validate" da nombre a un idioma que muchos programadores usan sin saberlo. Eli Bendersky lo revisa en Rust: en lugar de validar una y otra vez que un Vec no está vacío, se convierte a NonEmpty; en vez de comprobar que una ruta es absoluta, se usa AbsPathBuf. El sistema de tipos garantiza el invariante y el código cliente deja de manejar casos imposibles.

Cuando get_configuration_directories devolvía un Vec, simplemente lo validaba. Pero cuando devuelve un NonEmpty, el vector se transforma en otra entidad que lleva un significado adicional.
  1. articulatepang

    Prefiero una regla un poco más general: Haz que los estados ilegales sean irrepresentables. La regla "Parse, don't validate" es un caso especial de MISU.

    ¿Cuál es la diferencia? MISU se aplica incluso cuando no hay una transformación similar al parseo. Por ejemplo, si tienes una variable que representa el estado actual de una conexión de red, y digamos que puede estar Desconectada, o Conectada a alguna dirección IP (esto es una simplificación excesiva).

    Entonces una forma de hacerlo sería

    struct {

    connected: bool,

    peer_ip: int32

    }

    El problema es que esto nos permite representar un estado ilegal/sin sentido: estamos desconectados pero todavía hay algún peer_ip viejo y basura por ahí. Peor aún, podríamos haber escrito

    struct {

    connected: bool,

    peer_ip: Option<int32>

    }

    Ahora podríamos tener connected = true pero peer_ip = None.

    La solución es usar un tipo suma:

    type connection =

    Disconnected

    | Connected of int32

    (perdón por usar sintaxis inventada; espero que sea clara para cualquiera familiarizado con Rust.)

    "Haz que los estados ilegales sean irrepresentables" se aplica en todo tu programa, en cada interfaz entre módulos o funciones del programa, incluyendo pero no limitado al parseo de entrada.

  2. Fluorescence

    No estoy seguro de que ese tipo sea un buen consejo:

    pub struct NonEmpty<T> {

    pub head: T,

    pub tail: Vec<T>,

    }

    Tendrías que implementar manualmente los traits para soportar la ergonomía de los slices y la iteración, y una reasignación costosa si necesitas pasar la propiedad como un Vec:

    Yo esperaría:

    pub struct NonEmpty<T> {

    v: Vec<T>,

    }

    El constructor impondría la invariante y luego implementarías Deref y DerefMut para [T] para obtener len/is_empty/indexación/iteración normales, pasando como &[T] a otras funciones y mutando valores (lo que no puede romper la invariante).

    Para mutar la longitud preservando la invariante es a elección del consumidor, p. ej.

    - añadir .into_vec() para desenvolver/mutar/volver a envolver

    - añadir mutadores que preserven la invariante de tu elección

  3. OptionOfT

    Sobre la nota al pie:

    > [1] Otros lenguajes - como Go o Python - tienen una comprobación en tiempo de ejecución que lanza algún tipo de excepción o panic cuando se accede a lst[0] en una lista o slice vacía.

    Rust tiene lo mismo. Acceder a un `Vec` por índice pasa por el trait index: https://doc.rust-lang.org/std/ops/trait.Index.html#tymethod....

    Vec implementa Index aquí: https://github.com/rust-lang/rust/blob/d080e7dff1b0fc5454154...

    El Index de Vec delega en el de slice: https://github.com/rust-lang/rust/blob/d080e7dff1b0fc5454154...

    Slice delega en... intrinsics: https://github.com/rust-lang/rust/blob/d080e7dff1b0fc5454154...

    Lo cual inyecta una comprobación de límites: https://github.com/rust-lang/rust/blob/d080e7dff1b0fc5454154...

    La comprobación de límites: https://github.com/rust-lang/rust/blob/d080e7dff1b0fc5454154...

    Así que diría que la nota al pie no es correcta.

  4. jelder

    Esto es genial. Alexis King dijo en realidad que, si hubiera sabido lo popular que sería "Parse, Don't Validate", lo habría escrito en un lenguaje más ampliamente usado que Haskell.

  5. jph

    Buen artículo sobre las fortalezas de Rust con los tipos. Si te gusta esto, puede que tengas curiosidad por cómo construir tus propias capacidades de parseo. Me gustan los crates de Rust Winnow y Nom, y también los traits From e Into de Rust.

Más de este día

2026-09-27