# Introducción a Rust para crear aplicaciones seguras

**Categoría:** Programación · **Nivel:** Intermedio · **Lectura:** 13 min
**Publicada:** 2026-07-27 · **Actualizada:** 2026-07-27 · **Autoría:** Underc0de
**Versión HTML (canónica):** https://underc0de.org/guias/programacion/introduccion-a-rust/

Cinco reglas que el compilador hace cumplir, y con eso desaparece una familia entera de errores: sin recolector de basura y sin liberar memoria a mano. Acá están las reglas, lo que resuelven y lo que cuestan.

## Respuesta rápida

**Rust** elimina una familia entera de errores de memoria haciendo que el compilador se niegue a compilar el código que los contiene. Lo consigue con **cinco reglas**. Las tres de **propiedad**: cada valor tiene un dueño, solo un dueño a la vez, y cuando el dueño sale del ámbito el valor se libera. Y las dos de **referencias**: en un momento dado se puede tener *o bien* una referencia mutable *o bien* cualquier cantidad de inmutables, y las referencias siempre tienen que ser válidas. Con eso desaparecen el uso después de liberar, la doble liberación y las **carreras de datos** —que el propio libro dice que Rust previene «negándose a compilar»—. Todo sin recolector de basura.

## Qué problema resuelve

Durante décadas, quien programaba tuvo que elegir entre dos opciones incómodas: **administrar la memoria a mano**, con control total y la responsabilidad de no equivocarse nunca, o **delegarla a un recolector de basura**, con la comodidad de no pensar en ella y el costo de pausas impredecibles y menos control.

Rust propone una tercera vía: **que el compilador verifique, antes de ejecutar, que el manejo de memoria es correcto**. Si no puede probarlo, no compila. El mecanismo se llama propiedad, y el libro oficial lo define así: «la **propiedad** es un conjunto de reglas que gobiernan cómo un programa de Rust administra la memoria».

Los tres problemas que aborda, según el mismo libro: «saber qué partes del código usan qué datos en el montículo, minimizar la cantidad de datos duplicados en el montículo, y limpiar los datos sin usar para no quedarse sin espacio».

## Propiedad: las tres reglas

![Las reglas de Rust. Arriba, la definición de propiedad. A la izquierda, las tres reglas de propiedad. A la derecha, las dos reglas de las referencias. Debajo, la definición de carrera de datos y la afirmación de que Rust la previene negándose a compilar. Al final, lo que sí resuelve y lo que no.](../../assets/img/guias/introduccion-a-rust-propiedad.svg)

El libro las enumera en tres líneas, y vale memorizarlas porque todo lo demás se deriva de ellas:

1. **«Cada valor en Rust tiene un *dueño*.»** Siempre hay una variable responsable de ese valor.
2. **«Solo puede haber un dueño a la vez.»** La propiedad no se comparte: se transfiere o se presta.
3. **«Cuando el dueño sale del ámbito, el valor se libera.»** Y esta es la que reemplaza al recolector de basura *y* a la liberación manual.

La tercera merece detalle porque es donde está la elegancia del diseño. El libro lo explica así: «cuando una variable sale del ámbito, Rust llama por nosotros a una función especial. Esa función se llama `drop` [...] Rust llama a `drop` automáticamente en la llave de cierre».

> **Por qué eso es distinto de todo lo anterior.** No hay que **recordar liberar**, como en C, así que desaparecen las fugas por olvido y la doble liberación. Y no hay un **recolector recorriendo la memoria** en momentos impredecibles, así que el uso de recursos es determinista: sabés exactamente cuándo se libera cada cosa, porque está escrito en la estructura del código.

## Mover, clonar y copiar

La regla del dueño único tiene una consecuencia que sorprende a todo el mundo la primera vez: asignar un valor a otra variable puede **invalidar la original**.

```rust
let s1 = String::from("hola");
let s2 = s1;              // la propiedad se MUEVE a s2

println!("{s1}");          // ✗ Error: el valor fue movido

// Si de verdad querés dos copias independientes, se pide explícitamente
let s1 = String::from("hola");
let s2 = s1.clone();      // copia real, con su costo visible
println!("{s1} {s2}");     // ✓
```

Esa decisión de diseño tiene un efecto secundario valioso: **el costo de copiar es visible en el código**. En lenguajes donde copiar es implícito, una operación cara puede esconderse en una asignación inocente; en Rust, si ves `clone()`, sabés que ahí se está duplicando algo.

Los tipos simples que viven en la pila —números, booleanos— se copian en lugar de moverse, porque copiarlos es trivial. La distinción viene de dónde vive el dato: el libro recuerda que «todos los datos almacenados en la pila tienen que tener un tamaño conocido y fijo», y lo de tamaño desconocido o variable va al montículo.

## Préstamos: las dos reglas

Si la propiedad se moviera en cada llamada a función, programar sería insoportable. Para eso existen las **referencias**: permiten usar un valor sin tomar su propiedad. Y tienen dos reglas, textuales del libro:

> **Las reglas de las referencias**
>
> «En un momento dado, podés tener **o bien** una referencia mutable **o bien** cualquier cantidad de referencias inmutables.»
>
> «Las referencias siempre tienen que ser válidas.»

Dicho en una frase que se recuerda mejor: **muchos lectores o un escritor, nunca las dos cosas**. La segunda regla, la de validez, es lo que elimina las referencias a memoria ya liberada: el compilador sigue la vida de cada valor y rechaza cualquier referencia que pueda sobrevivirlo.

## Carreras de datos

Acá está el resultado más valioso de todo el sistema, y el libro lo explica con precisión. Primero define el problema: una **carrera de datos** ocurre cuando se dan estas tres condiciones:

- «Dos o más punteros acceden al mismo dato al mismo tiempo.»
- «Al menos uno de los punteros se está usando para escribir en el dato.»
- «No hay ningún mecanismo en uso para sincronizar el acceso al dato.»

Y después la consecuencia: «las carreras de datos causan comportamiento indefinido y pueden ser difíciles de diagnosticar y arreglar cuando estás tratando de rastrearlas en tiempo de ejecución; **Rust previene este problema negándose a compilar código con carreras de datos**».

Fijate cómo se conecta: la restricción de **una sola referencia mutable** hace lógicamente imposible que se cumplan las tres condiciones a la vez. El libro lo dice explícitamente: «el beneficio de tener esta restricción es que Rust puede prevenir carreras de datos en tiempo de compilación».

Para quien haya depurado un problema de concurrencia intermitente, esto es la característica que justifica todo el resto: esos errores no aparecen en las pruebas, no se reproducen a pedido y consumen días. Que el compilador los rechace de antemano cambia la ecuación por completo.

## Errores sin excepciones

Rust no tiene excepciones. Un valor que puede faltar o una operación que puede fallar se representan **en el tipo**, y el sistema de tipos obliga a atender ese caso antes de seguir:

```rust
// La firma dice que puede fallar: no hay error invisible
fn leer_config(ruta: &str) -> Result<Config, ConfigError> {
    // …
}

// No se puede usar el resultado sin decidir qué hacer con el error
match leer_config("app.toml") {
    Ok(config) => arrancar(config),
    Err(e)      => eprintln!("no se pudo leer la configuración: {e}"),
}
```

La diferencia con las excepciones es de **visibilidad**: en la firma está escrito que la función puede fallar. No hay errores que se propaguen por lugares que nadie declaró, ni bloques que capturan todo «por si acaso» y esconden el problema real. El costo es más ceremonia; el beneficio es que la ruta de error es parte del diseño y no una ocurrencia posterior.

## Cargo y el ecosistema

Una parte importante de la experiencia de usar Rust no es el lenguaje sino **cargo**, su herramienta oficial, que resuelve en un solo lugar lo que en otros ecosistemas está repartido entre varias:

```bash
cargo new mi-proyecto     # crea el proyecto con su estructura
cargo check               # comprueba sin compilar del todo: rapidísimo
cargo build --release     # compila optimizado
cargo test                # ejecuta las pruebas
cargo fmt                 # formatea con el estilo oficial
cargo clippy              # sugerencias del analizador oficial
cargo doc --open          # genera y abre la documentación
```

Dos de estos merecen mención. **`cargo check`** es el comando que más se usa mientras se aprende: comprueba que el código sea válido sin generar el binario, así que el ciclo de corregir errores del compilador es mucho más rápido. Y **`cargo clippy`** no solo detecta problemas: explica cómo se escribiría de forma más idiomática, lo que en la práctica funciona como una clase continua.

## Cuándo conviene y cuándo no

| Situación | ¿Conviene? | Por qué |
|---|---|---|
| Herramientas de línea de comandos | **Sí** | Binario único sin dependencias de ejecución, rápido de arrancar |
| Componentes de sistema y código embebido | Sí | Control de recursos sin recolector de basura |
| Servicios con requisitos altos de rendimiento | Sí | Uso de memoria predecible, sin pausas de recolección |
| Bibliotecas que otros lenguajes van a llamar | Sí | Se integra bien como componente nativo |
| Compilar a [WebAssembly](../desarrollo-con-webassembly/index.md) | Sí | Es uno de los lenguajes con mejor soporte para ese destino |
| Primer lenguaje para aprender a programar | **No** | Suma la dificultad del lenguaje a la de aprender a programar |
| Un script que se usa una vez | No | El costo de las garantías no se recupera |
| Prototipos exploratorios que se van a tirar | No | El compilador exige rigor que ese código no necesita |

## La curva de aprendizaje

Conviene decirlo sin adornos: **Rust cuesta más de aprender que la mayoría de los lenguajes**. Pero la dificultad tiene una forma particular que ayuda a atravesarla.

No es que el lenguaje sea confuso: es que el compilador **rechaza código que en otros lenguajes habría compilado**. Y esa experiencia, al principio, se siente como pelear con la herramienta. El cambio de encuadre que hace la diferencia es entender que **cada rechazo es un error que no vas a tener que depurar en producción**: el trabajo no aumentó, se movió de lugar y se hizo visible.

Dos cosas que ayudan de verdad: los **mensajes del compilador son inusualmente buenos** —explican la causa, señalan la línea y muchas veces sugieren la corrección exacta—, y `cargo check` hace que el ciclo de intento y corrección sea rápido. Vale leer los mensajes completos en lugar de buscarlos en internet: casi siempre traen la respuesta.

## Errores frecuentes

- **Clonar para hacer callar al compilador.** Funciona y esconde el problema de diseño; suele haber una solución con referencias.
- **Pelear con el verificador de préstamos en lugar de rediseñar.** Cuando el compilador insiste, casi siempre está señalando que la estructura de datos no es la adecuada.
- **Buscar los mensajes de error en internet antes de leerlos.** Son largos porque traen la explicación y la sugerencia.
- **Empezar por lo avanzado.** Propiedad y préstamos primero; la concurrencia y los rasgos complejos después.
- **Usar el modo sin comprobaciones para avanzar.** Existe para casos que lo justifican, no para saltear el aprendizaje.
- **Suponer que seguro con la memoria significa seguro en general.** Elimina una familia de errores, no la lógica equivocada ni las vulnerabilidades de otro tipo.
- **Elegirlo por moda.** Si el proyecto no necesita sus garantías, el costo no se recupera.

## Preguntas frecuentes

### ¿Qué significa que Rust sea seguro con la memoria?

Que una familia entera de errores no puede llegar a ejecutarse, porque el compilador rechaza el código que los contiene. Esa familia incluye usar memoria ya liberada, liberarla dos veces, leer más allá del final de un arreglo y las carreras de datos. Lo logra con cinco reglas que el compilador hace cumplir: las tres de propiedad y las dos de referencias. Lo notable es que lo consigue sin recolector de basura y sin que haya que liberar memoria a mano, porque el compilador sabe exactamente cuándo termina la vida de cada valor.

### ¿Cuáles son las reglas de propiedad?

El libro oficial las enumera en tres líneas: cada valor en Rust tiene un dueño; solo puede haber un dueño a la vez; y cuando el dueño sale del ámbito, el valor se libera. La tercera es la que reemplaza tanto al recolector de basura como a la liberación manual: al cerrarse el bloque, Rust llama automáticamente a una función de liberación. De ahí sale la propiedad más práctica del lenguaje: no hay que recordar liberar nada, y tampoco hay una pausa impredecible de recolección.

### ¿Qué son los préstamos y por qué solo uno mutable?

Un préstamo es una referencia a un valor sin tomar su propiedad, y las reglas son dos: en un momento dado se puede tener o bien una referencia mutable o bien cualquier cantidad de referencias inmutables, y las referencias siempre tienen que ser válidas. La restricción de una sola referencia mutable no es un capricho: es exactamente lo que permite prevenir carreras de datos en tiempo de compilación, porque hace imposible que dos partes del código escriban el mismo dato a la vez sin coordinación.

### ¿Cómo se manejan los errores sin excepciones?

Con tipos que representan la posibilidad de fallo, y el sistema de tipos obliga a atenderlos. Una función que puede fallar devuelve un valor que es o bien el resultado o bien el error, y no se puede usar el resultado sin decidir antes qué hacer con la otra posibilidad. La diferencia con las excepciones es de visibilidad: en la firma de la función está escrito que puede fallar, así que no hay errores invisibles que se propagan sin que nadie los declare ni bloques que capturan todo por si acaso.

### ¿Es difícil aprender Rust?

Tiene una curva de aprendizaje real y conviene decirlo sin adornos, pero la dificultad está concentrada y es de un tipo particular: el compilador rechaza código que en otros lenguajes habría compilado, y al principio eso se siente como pelear con una herramienta. Lo que ayuda es un cambio de encuadre: cada rechazo es un error que no vas a tener que depurar en producción. Y los mensajes de error del compilador son inusualmente buenos, con explicación de la causa y sugerencia de corrección.

### ¿Para qué conviene usar Rust y para qué no?

Conviene donde el control de recursos y las garantías importan más que la velocidad de escritura: componentes de sistema, herramientas de línea de comandos, servicios de alto rendimiento, código embebido, motores y bibliotecas que otros lenguajes van a llamar, y compilación a WebAssembly. No conviene como primer lenguaje para aprender a programar, ni para un script de una vez, ni para prototipos donde el objetivo es explorar rápido y el código se va a tirar. Ahí el costo de las garantías no se recupera.

## Fuentes

Documentación oficial y material de la comunidad consultados para esta guía. Fecha de consulta: 27 de julio de 2026.

### Aportes de la comunidad Underc0de

1. **Underc0de, foro.** [Sección Programación General](https://underc0de.org/foro/programacion-general/). Hilos de la comunidad sobre lenguajes compilados y programación de sistemas.
2. **Underc0de, foro.** [Sección C y C++](https://underc0de.org/foro/c-c/). El contexto donde los errores de memoria que Rust previene aparecen a diario.

### Documentación oficial

3. **Rust.** [What is Ownership?](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html). Definición de propiedad, las tres reglas, la pila y el montículo, y la liberación automática, citadas textualmente.
4. **Rust.** [References and Borrowing](https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html). Las dos reglas de las referencias, la definición de carrera de datos y la prevención en tiempo de compilación, citadas textualmente.
5. **Rust.** [The Rust Programming Language](https://doc.rust-lang.org/book/). El libro oficial completo, punto de entrada recomendado para aprender el lenguaje.
6. **Rust.** [The Cargo Book](https://doc.rust-lang.org/cargo/). Documentación de la herramienta oficial de compilación y gestión de dependencias.

---

Esta guía forma parte de un proyecto de la comunidad Underc0de para organizar conocimiento técnico en español. Fuente canónica: https://underc0de.org/guias/programacion/introduccion-a-rust/
