Un tipo por variante: el patrón de Rust que hace más seguras las rutas
El autor de path_facts cambió std::path::Component por un struct propio para cada variante y consiguió que el compilador verifique qué se puede unir a una ruta

Un desarrollador de la librería path_facts ha contado cómo dejó de usar el enum std::path::Component de Rust y creó un tipo propio para cada una de sus variantes. El objetivo no era el rendimiento ni la ergonomía, sino que el compilador le obligue a demostrar qué tiene entre manos antes de tocar una ruta.
Component es lo que devuelve cualquier referencia a std::path::Path cuando se recorre como iterador: /tmp/hello se descompone en RootDir, Normal("tmp") y Normal("hello"). El enum sirve para partir rutas, pero una función que acepte "un componente" a secas es demasiado amplia: cabe cualquiera de las variantes y eso no dice nada, ni al lector ni al compilador.
Un struct por variante
La propuesta es mecánica: si el enum tiene N variantes, se crea un struct para cada una. NormalComponent(OsString) solo se construye desde un Component::Normal. Con eso, una firma como join_normal(&self, path: &NormalComponent) -> AbsPath garantiza que unir algo a una ruta absoluta devuelve otra ruta absoluta. Antes la firma recibía un &OsStr, que puede ser cualquier cosa: llevar un .. o ser una ruta absoluta y provocar que el join reemplace el destino. El mismo tipo sirve para representar las entradas de un directorio.
El autor lo llama el movimiento obvio. Si resulta útil saber que tienes una cosa entre N posibles, también lo es saber que tienes una concreta de esas N. Un enum es un tipo suma, así que los componentes individuales de esa suma también merecen ser un tipo. Antes había llegado a AbsPath, RelativePath y CanonicalPath, y los encontró útiles pero todavía demasiado genéricos.
El caso de Windows
Trabajar con rutas mezcla dos cosas: el valor léxico y la ubicación física, y no siempre coinciden. Una ruta léxicamente relativa puede ser un symlink con destino absoluto. Normalizar tiene consecuencias: pedir metadatos de /path/to/location/skipped/.. falla si skipped no existe o no es un directorio, mientras que canonicalizar primero produce /path/to/location y ahí sí funciona.
Una ruta absoluta no está normalizada, así que puede llevar .. y . dentro. Windows añade las rutas verbatim del estilo \?\C:\windows, donde esos dos componentes se tratan como texto literal, y eso convierte en insegura una llamada del tipo canonicalize(canonicalize(&path).join(&other)) si other contiene un .. : lo más probable es que la búsqueda falle. Lo que salva el patrón es que el parseo de Component es consistente: un .. siempre sale como ParentDir, nunca como Normal("..").
El toque final es una función que recorre una ruta componente a componente: fn up(position: Reached, name: ParentDirComponent) -> (Step, Reached). La firma obliga a demostrar que se tiene un .. en la mano antes de llamarla. El autor reconoce que no es nada del otro mundo, pero que esa confianza pedante resulta agradable.
Escribió ese código a mano, sin conexión, mientras esperaba en un coche a que terminara el entrenamiento de fútbol de sus hijos. La librería está abierta y el patrón se puede copiar tal cual, aunque solo sea para revisar cuántas funciones del propio código aceptan un OsStr donde en realidad solo esperan una cosa concreta.

