Saltar al contenido principal

Estilo de código

Convenciones de código en Xindeler. El 90% se aplica automáticamente con las herramientas — no hay que memorizarlo.

Herramientas obligatorias​

rustfmt​

Formatea el código automáticamente. No hay discusión sobre estilo de formato — rustfmt tiene la última palabra.

cargo fmt

La configuración está en rustfmt.toml en la raíz. Ejecutá cargo fmt antes de cada commit.

clippy​

Linter de Rust. Detecta patrones problemáticos, código ineficiente y errores comunes.

cargo clippy -- -D warnings

El flag -D warnings trata los warnings como errores — es lo que corre el CI. Tu código debe pasar sin warnings antes de abrir el PR.

Algunos lints útiles que clippy detecta en este proyecto:

// ❌ clippy::clone_on_copy
let x = some_u32.clone(); // u32 implementa Copy, .clone() es innecesario

// ✅
let x = some_u32;

// ❌ clippy::needless_pass_by_value
fn process(items: Vec<Item>) { ... } // si no necesitás ownership

// ✅
fn process(items: &[Item]) { ... }

Convenciones de nombres​

Rust tiene convenciones fuertes y el compilador avisa cuando no se cumplen:

ElementoConvenciónEjemplo
Tipos, traits, enumsUpperCamelCaseHealthComponent, CombatSystem
Funciones, métodos, variablessnake_caseapply_damage, max_health
ConstantesSCREAMING_SNAKE_CASEMAX_PLAYERS, BASE_DAMAGE
Módulos, cratessnake_casecombat_sys, rtsim
Archivossnake_casecombat_sys.rs, npc_mind.rs

Nombres en este proyecto​

Algunas convenciones específicas del codebase:

  • Los sistemas ECS terminan en _sys o System: CombatSystem, ai_sys.rs
  • Los componentes ECS terminan en Comp o son sustantivos simples: Health, Energy, Pos
  • Los archivos RON usan el nombre del asset en snake_case: iron_sword.ron
  • Los eventos del servidor usan ServerEvent::Variante

Comentarios​

Escribí comentarios solo cuando el por qué no es obvio. El qué ya lo dice el código.

// ❌ no aporta nada
// Incrementa el contador de kills
player.kills += 1;

// ✅ explica una decisión no obvia
// Usamos saturating_add para evitar overflow en counters de larga sesión
player.kills = player.kills.saturating_add(1);

Para funciones públicas en common/, sí se esperan doc comments (///) con una línea de descripción:

/// Returns the effective damage after applying armor mitigation.
pub fn mitigate_damage(raw: f32, armor: f32) -> f32 {
raw * (1.0 - armor.min(0.9))
}

Manejo de errores​

  • Usá ? para propagar errores en lugar de .unwrap() en código de producción
  • .unwrap() y .expect() están permitidos en tests y en situaciones donde el invariante es imposible de violar (documentarlo con un comentario)
  • Preferí Option sobre valores centinela (-1, "", etc.)
// ❌ panic en producción
let item = inventory.get(slot).unwrap();

// ✅
let Some(item) = inventory.get(slot) else {
return; // slot vacío, nada que hacer
};

Performance​

  • Evitá allocaciones en sistemas ECS que corren cada tick — usá slices y referencias donde sea posible
  • Preferí &str sobre String en parámetros de funciones
  • Los sistemas paralelos de specs no deben contener Mutex — el paralelismo lo maneja el scheduler del ECS
  • Perfilá antes de optimizar: cargo flamegraph o perf en Linux