Codificación en Rust
Flujo de trabajo
-
Orientación. Lee las fuentes de verdad del proyecto antes de escribir:
rust-toolchain.toml,Cargo.toml(edition,rust-version,[lints],[workspace]),clippy.toml,rustfmt.toml,.cargo/config.toml, y la configuración de CI,justfile,Makefile, oxtaskpara los comandos de verificación canónicos. Lee el módulo que estás modificando y sus vecinos; reutiliza sus ayudantes, tipos de error y patrones. Terminado cuando: puedes nombrar la edición, la MSRV, la política de lints, cada entrada dedisallowed-methods/disallowed-types, y los comandos de verificación del proyecto. -
Modela los tipos. Diseña los datos antes que el flujo de control, siguiendo Tipos. Si cambias la API pública de una biblioteca: lee
references/api.md. Terminado cuando: cada invariante del que depende el cambio está codificado en un tipo, o mantenido por unexpectcuyo mensaje lo nombra. -
Implementa en un bucle cerrado. Trabaja en pasos pequeños y ejecuta
cargo checkdespués de cada uno. Lee el diagnóstico completo: su líneahelp:suele nombrar la corrección, yrustc --explain E0502expande un código de error. Para la API de una dependencia, lee su fuente en la versión bloqueada (Cargo.lock) bajo$CARGO_HOME/registry/src/. Los cambios masivos de nombre se realizan mediante el renombrado de rust-analyzer oast-grep;cargo clippy --fixaplica sugerencias aplicables automáticamente. Corrección de errores: escribe la prueba de regresión primero y observa que se ponga roja. Terminado cuando: el cambio compila sin advertencias en las líneas que cambiaste, y cualquier prueba de regresión se puso roja antes de la corrección y verde después. -
Verifica. Ejecuta los comandos del proyecto del paso 1; de lo contrario:
cargo fmt --all cargo clippy --workspace --all-targets --all-features -- -D warnings cargo test --workspace --all-features- Proyecto sin política
[lints]: también ejecutacargo clippy --all-targets -- -W clippy::pedanticy corrige los aciertos en las líneas que cambiaste. - Funcionalidades mutuamente excluyentes: reemplaza
--all-featurescon cada conjunto de funcionalidades que CI pruebe. cargo nextestomite doctests: añadecargo test --doc.- Documentación pública cambiada:
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features. unsafetocado: ejecutareferences/unsafe.md.- Corrige los lints en la causa. Cuando la supresión sea correcta, limita
#[expect(clippy::lint_name, reason = "...")]al elemento más pequeño. Terminado cuando: cada comando sale con 0, las líneas que cambiaste no generan advertencias, y cada nueva supresión lleva unreason.
- Proyecto sin política
-
Autorevisa el diff. Vuelve a leer el
git diffcompleto, no tu memoria del mismo, contra Reglas y las reglas de cada archivo de referencia cuya rama toque el diff. Terminado cuando: cada hunk es necesario para la tarea, el código prototipo ydbg!/todo!han desaparecido, los comentarios de documentación coinciden con el comportamiento, y cada regla aplicable se cumple para cada hunk.
Revisando código de otra persona: ejecuta el paso 1, ejecuta los comandos del paso 4 cuando puedas, luego aplica el paso 5 a su diff. Reporta cada hallazgo como path:line, la regla y la corrección.
Reglas
Ownership
- Toma prestado en parámetros:
&str,&[T],&Path,&T. TomaString,Vec<T>oTpor valor solo cuando la función los almacena o consume. - Cada
.clone()se gana su lugar: el destinatario necesita ownership, o el valor es un handle barato (Arc::clone(&handle)). - Cuando el borrow checker se opone, reestructura primero: acorta el préstamo, toma prestados campos disjuntos por separado,
std::mem::take/replace, índices en unVec. El ownership mutable compartido (Rc<RefCell<_>>,Arc<Mutex<_>>) es el último recurso. - Devuelve
impl Iterator<Item = T>cuando el llamante solo itera.
Tipos
- Haz que los estados ilegales no sean representables: un
enumque lleva datos por estado, en lugar de banderasboolmás camposOption; newtypes para IDs, unidades y valores validados (struct Port(NonZeroU16)). - Analiza, no valides: convierte la entrada no confiable una vez en el límite (
TryFrom,FromStr,#[serde(try_from = "Raw")]) y pasa el tipo analizado hacia adentro. - Haz matching exhaustivo en enums que posees. Un brazo
_es correcto solo cuando una variante futura debería tomar esa rama también. - Convierte enteros con
From/TryFrom;astrunca e invierte el signo silenciosamente.
Errores y panics
- Biblioteca: un enum
thiserrorpor dominio de fallo. Binario:anyhow::Resultcon.context(...). - Los panics significan errores.
unwrap()vive en pruebas; el código de producción usa?oexpect("...")indicando el invariante que hace imposible el fallo.
Modismos actuales
Los patrones antiguos dominan los datos de entrenamiento. Prefiere la forma actual cuando rust-version lo permita; los nuevos crates usan edition = "2024". clippy::incompatible_msrv detecta solo APIs nuevas de std; las características del lenguaje y Cargo necesitan cargo +<msrv> check.
| Preferir | Sobre | Desde |
|---|---|---|
std::sync::LazyLock, OnceLock | lazy_static!, once_cell | 1.80, 1.70 |
let Some(x) = opt else { return ... }; | match + retorno temprano | 1.65 |
if let Some(x) = a && x > 0 | if let anidado | 1.88, edition 2024 |
async fn nativo en traits | #[async_trait], aún necesario para dyn Trait | 1.75 |
| closures `async | {}, bounds AsyncFn` | |
#[expect(lint, reason = "...")] | #[allow(lint)] | 1.81 |
&raw const x, &raw mut x | ptr::addr_of! | 1.82 |
opt.is_some_and(f), opt.is_none_or(f) | map_or(false, f), map_or(true, f) | 1.70, 1.82 |
std::hint::black_box | criterion::black_box | 1.66 |
[lints] en Cargo.toml para política compartida | #![warn(...)] en cada raíz de crate | 1.74 |
directivas cargo:: de build-script | cargo: | 1.77 |
Documentación y comentarios
- Los elementos públicos obtienen docs
///:# Errorsen funciones que pueden fallar,# Panicsen funciones que pueden entrar en pánico, ejemplos como doctests usando?. - Los comentarios explican por qué; el código muestra qué.
Dependencias
- Reutiliza lo que el workspace ya depende (
[workspace.dependencies],cargo tree -i <crate>), y prefiere std cuando cubre la necesidad. - Añade con
cargo add(consciente de MSRV), habilitando solo las funcionalidades que uses.
Referencias
Carga la que coincida con la rama en la que estás:
references/api.md: diseñar o cambiar la API pública de una biblioteca, nombres, implementaciones de traits, semver.references/errors.md: definir tipos de error, propagación, panic vsResult.references/async.md: async, tokio, hilos, canales, locks, atómicos.references/unsafe.md:unsafe, FFI, punteros crudos,Send/Syncmanual.references/cargo.md:Cargo.toml, workspaces, funcionalidades, perfiles, scripts de construcción, selección de crates.references/lints.md: configurar la configuración de lints para un crate o workspace.references/testing.md: escribir pruebas, pruebas de propiedades y snapshot, benchmarks.references/perf.md: optimizar un camino caliente medido.