← SEARCHABLE ARCHIVE
Rust · 2026-08-12

Rust de los fundamentos al dominio — Lección 10

Limpiando su diseño de Rust con límites y pruebas seguras

INTERACTIVE READING MODE →

Los artículos escritos en este blog fueron creados con el objetivo de enseñar. Además, la información del artículo puede ser incorrecta, así que tenga cuidado.

Usar bien Rust no implica el uso complicado de todas las vidas útiles y punteros inteligentes. Se trata de mantener pequeños los límites de propiedad, los errores, las operaciones concurrentes y las áreas inseguras y hacer que el comportamiento sea comprobable.

Determinar el propietario de un estado

struct GameState {
    players: Vec<Player>,
    monsters: Vec<Monster>,
}

impl GameState {
    fn attack(&mut self, player_index: usize, monster_index: usize) -> bool {
        let Some(player) = self.players.get(player_index) else {
            return false;
        };
        let Some(monster) = self.monsters.get_mut(monster_index) else {
            return false;
        };

        monster.take_damage(player.attack);
        true
    }
}

GameState posee el estado de combate y el método toma prestados sólo los campos que necesita brevemente. La reducción del estado mutable global también simplifica la vida útil de la referencia y la simultaneidad.

Poner funciones externas después del rasgo

trait SaveStore {
    fn save(&self, state: &GameState) -> Result<(), SaveError>;
    fn load(&self) -> Result<Option<GameState>, SaveError>;
}

fn save_game(store: &impl SaveStore, state: &GameState) -> Result<(), SaveError> {
    store.save(state)
}

Los detalles de implementación de archivos y redes no están incluidos en el código de regla. En las pruebas, puede implementar almacenes de memoria para crear fracasos y éxitos según lo desee.

Reducir los límites de lo inseguro

fn value_at(values: &[i32], index: usize) -> Option<i32> {
    values.get(index).copied()
}

No hay razón para usar unsafe si se puede resolver con una API segura. Si es necesario que no sea seguro debido a FFI o a una optimización de bajo nivel, debe ocultarse en una función pequeña y las condiciones que debe cumplir la persona que llama deben garantizarse con tipos y comprobaciones seguros.

inseguro no es una función que desactive todas las comprobaciones del compilador. Esto es una indicación de que el desarrollador demostrará directamente algunas responsabilidades, como la desreferencia del puntero.

Probando el contrato

#[test]
fn invalid_target_does_not_change_state() {
    let mut state = sample_state();
    let before = state.monsters[0].health;

    assert!(!state.attack(0, 99));
    assert_eq!(state.monsters[0].health, before);
}

Las pruebas verifican los contratos externos, como si se mantiene el estado cuando fallan, en lugar de la implementación interna. El punto fuerte de Rust no es que esté absolutamente libre de errores, sino que reduce muchos estados incorrectos antes de la ejecución con comprobaciones de tipo y compilación.