Rust es un lenguaje de programación de sistema desarrollado por Mozilla. Destaca por su seguridad, rendimiento y concurrencia.
Instalación y configuración
Instalar Rust
Rust utiliza una herramienta llamada rustup
para la instalación y administración de versiones.
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Comprobar instalación
Verifica que Rust y Cargo (el gestor de paquetes) estén instalados correctamente.
rustc --version
cargo --version
Actualizar Rust
Para mantener Rust actualizado:
rustup update
Instalar herramientas adicionales
Rust tiene soporte para linters y formateadores como rustfmt
y clippy
.
rustup component add rustfmt
rustup component add clippy
Estructura básica de un programa
Hola, Mundo
El programa más simple en Rust imprime “Hola, Mundo” en la consola.
fn main() {
println!("Hola, Mundo!");
}
Estructura básica de un proyecto
Rust utiliza Cargo
para gestionar proyectos. Crear un nuevo proyecto:
cargo new nombre_proyecto
El archivo principal estará en src/main.rs
, y el proyecto incluirá un Cargo.toml
para definir dependencias y configuraciones.
Compilar y ejecutar
cargo build # Solo compila
cargo run # Compila y ejecuta
cargo check # Verifica errores de compilación sin generar binario
Variables y tipos de datos
Declaración de variables
let
declara una variable. Por defecto son inmutables, pero se puede agregar mut
para hacerlas mutables.
let nombre = "Luis"; // Variable inmutable
let mut edad = 30; // Variable mutable
Declaración de constantes
const
declara una constante. Debe tener un tipo explícito y su valor no puede cambiar.
const PI: f64 = 3.14159;
Inmutabilidad
Por defecto, las variables en Rust son inmutables. Para que sean mutables, se utiliza mut
.
let x = 5; // Inmutable
let mut y = 10; // Mutable
Tipos básicos
Rust es un lenguaje fuertemente tipado. Algunos de los tipos básicos son:
- Enteros:
i8
,i16
,i32
,i64
,i128
,u8
,u16
,u32
,u64
,u128
- Flotantes:
f32
,f64
- Booleanos:
bool
- Caracteres:
char
let numero: i32 = 42;
let nombre: &str = "Rust";
let flotante: f64 = 3.14;
let es_verdad: bool = true;
let caracter: char = 'R';
Cadena de texto
String
Las cadenas de texto en Rust (String
) son gestionadas dinámicamente y se pueden modificar.
let mut saludo = String::from("Hola");
saludo.push_str(", mundo!");
str
Cadena de texto estática.
let texto_estatico: &str = "Hola, mundo!";
Control de flujo
Condicionales
if-else
El condicional if
en Rust se usa de manera similar a otros lenguajes.
if x > 5 {
println!("x es mayor que 5");
} else {
println!("x es menor o igual que 5");
}
match
Permite hacer coincidencia de patrones (pattern matching).
let numero = 5;
match numero {
1 => println!("Es uno"),
2..=5 => println!("Está entre 2 y 5"),
_ => println!("Otro número"),
}
Bucles
Bucle for
Bucle para iterar sobre rangos y colecciones.
for i in 1..5 {
println!("i: {}", i); // i toma los valores de 1 a 4
}
Bucle while
Bucle que se ejecuta mientras se cumple una condición.
let mut contador = 0;
while contador < 5 {
println!("contador: {}", contador);
contador += 1;
}
Bucle loop
Bucle infinito.
loop {
println!("Este ciclo es infinito");
break; // Termina el ciclo
}
Funciones
Definición de funciones
Las funciones en Rust se declaran con fn
y pueden retornar valores. Los tipos de parámetros y de retorno deben especificarse explícitamente.
fn suma(a: i32, b: i32) -> i32 {
a + b
}
let resultado = sumar(5, 3);
Funciones de Cierre (Closures)
Las closures en Rust son funciones anónimas que pueden capturar variables del entorno que las rodea.
let sumar_uno = |x| x + 1;
let resultado = sumar_uno(5);
println!("Resultado: {}", resultado); // Resultado: 6
Estructuras de Datos
Tuplas
Las tuplas son colecciones de elementos de diferentes tipos con un tamaño fijo.
let tupla: (i32, f64, &str) = (42, 3.14, "Hola");
let (x, y, z) = tupla;
println!("x: {}, y: {}, z: {}", x, y, z); // x: 42, y: 3.14, z: Hola
Structs
Los structs permiten crear tipos de datos personalizados con múltiples campos.
struct Persona {
nombre: String,
edad: i32,
}
let luis = Persona {
nombre: String::from("Luis"),
edad: 30,
};
println!("Nombre: {}, Edad: {}", luis.nombre, luis.edad);
Vectores
Los vectores son colecciones de elementos del mismo tipo que pueden crecer o disminuir en tamaño.
let mut v: Vec<i32> = Vec::new();
v.push(1);
v.push(2);
v.push(3);
HashMap
Los HashMaps almacenan pares clave-valor.
use std::collections::HashMap;
let mut mapa = HashMap::new();
mapa.insert(String::from("clave"), 10);
Traits
Los traits en Rust definen comportamientos comunes que un tipo puede implementar. Es similar a las interfaces en otros lenguajes.
trait Saludable {
fn saludar(&self) -> String;
}
struct Persona {
nombre: String,
}
impl Saludable for Persona {
fn saludar(&self) -> String {
format!("Hola, soy {}", self.nombre)
}
}
let luis = Persona { nombre: String::from("Luis") };
println!("{}", luis.saludar()); // Hola, soy Luis
Propiedad y referencias
Rust maneja la memoria con un sistema de propiedad que garantiza seguridad en tiempo de compilación.
Reglas de Propiedad
- Cada valor en Rust tiene un único propietario.
- Cuando el propietario sale de alcance, el valor es liberado.
- Se puede pasar referencias a los datos, pero deben cumplir con las reglas de préstamos y vidas.
Mover datos
Mover transfiere la propiedad del valor.
let s1 = String::from("Hola");
let s2 = s1; // `s1` ya no es válido, la propiedad pasa a `s2`
Clonar datos
Si necesitas copiar los datos, se usa clone
.
let s1 = String::from("Hola");
let s2 = s1.clone(); // Copia profunda de `s1`
Referencias y préstamos
Rust garantiza la seguridad de memoria a través del sistema de Ownership. Cada valor en Rust tiene un propietario y solo puede haber un propietario a la vez.
Para prestar un valor sin transferir propiedad, se usan referencias.
fn imprime(s: &String) {
println!("{}", s);
}
let cadena = String::from("Hola");
imprime(&cadena); // Pasa una referencia, no transfiere propiedad
Lifetimes
El concepto de lifetimes en Rust asegura que las referencias no vivan más tiempo que los datos a los que hacen referencia.
fn mas_largo<'a>(s1: &'a str, s2: &'a str) -> &'a str {
if s1.len() > s2.len() {
s1
} else {
s2
}
}
Manejo de errores
Rust maneja los errores de forma explícita a través del tipo Result
y la opción Option
.
Result
Result
se usa para operaciones que pueden fallar. Tiene dos variantes: Ok
y Err
.
fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
Err(String::from("División por cero"))
} else {
Ok(a / b)
}
}
Option
Option
se usa para valores que pueden ser nulos (None
).
fn encontrar_valor(vec: &Vec<i32>, x: i32) -> Option<usize> {
vec.iter().position(|&val| val == x)
}
Concurrencia en Rust
Rust ofrece concurrencia segura con hilos (threads
) y permite utilizar técnicas avanzadas como canales y locks para evitar condiciones de carrera.
Hilos
Rust utiliza el paquete std::thread
para crear y gestionar hilos.
use std::thread;
use std::time::Duration;
fn main() {
let handle = thread::spawn(|| {
for i in 1..5 {
println!("Hola desde el hilo");
thread::sleep(Duration::from_millis(1));
}
});
handle.join().unwrap();
}
Mensajería entre hilos con canales
Rust ofrece canales (channels
) para la comunicación entre hilos.
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let valor = String::from("Mensaje");
tx.send(valor).unwrap();
});
let recibido = rx.recv().unwrap();
println!("Recibido: {}", recibido);
}
Macros en Rust
Rust tiene soporte para macros que permiten generar código durante la compilación.
Definir macros
macro_rules! mi_macro {
($val:expr) => {
println!("El valor es: {}", $val);
};
}
mi_macro!(10);
Carga y pruebas en Rust
Rust facilita el desarrollo mediante pruebas integradas y compilación condicional.
Pruebas unitarias
#[cfg(test)]
mod tests {
#[test]
fn prueba_basica() {
assert_eq!(2 + 2, 4);
}
}
Ejecutar pruebas
cargo test
Cargo y manejo de dependencias
Cargo.toml
El archivo Cargo.toml
gestiona las dependencias y configuraciones del proyecto.
[dependencies]
rand = "0.8"
serde = { version = "1.0", features = ["derive"] }