Sintaxis Básica de Rust
En este capítulo exploraremos los fundamentos de la sintaxis de Rust. Aprenderás sobre variables, funciones, comentarios y las convenciones básicas del lenguaje.
Tu Primer Programa
fn main() {
println!("¡Hola, mundo!");
}
Este simple programa ilustra varios conceptos importantes:
fn define una función
main es el punto de entrada del programa
println! es un macro (nota el !)
- Los bloques de código van entre llaves
{}
Variables y Mutabilidad
En Rust, las variables son inmutables por defecto. Esta es una característica de seguridad fundamental.
Variables Inmutables
fn main() {
let x = 5;
println!("El valor de x es: {x}");
// x = 6; // ¡Esto causaría un error de compilación!
}
Variables Mutables
Para hacer una variable mutable, usa la palabra clave mut:
fn main() {
let mut x = 5;
println!("El valor de x es: {x}");
x = 6; // ¡Ahora esto es válido!
println!("El valor de x es: {x}");
}
Constantes
Las constantes son siempre inmutables y deben tener un tipo explícito:
const TRES_HORAS_EN_SEGUNDOS: u32 = 60 * 60 * 3;
fn main() {
println!("Tres horas son {} segundos", TRES_HORAS_EN_SEGUNDOS);
}
Diferencias entre constantes y variables inmutables:
- Las constantes se evalúan en tiempo de compilación
- Se pueden declarar en cualquier scope, incluyendo el global
- Solo pueden asignarse a expresiones constantes
- Por convención, se nombran en MAYÚSCULAS con guiones bajos
Shadowing (Sombreado)
Puedes declarar una nueva variable con el mismo nombre que una anterior:
fn main() {
let x = 5;
let x = x + 1; // Nueva variable x que "sombrea" la anterior
{
let x = x * 2; // Otra nueva variable x en este scope
println!("El valor de x en el scope interno es: {x}"); // 12
}
println!("El valor de x es: {x}"); // 6
}
Ventajas del shadowing:
- Puedes cambiar el tipo de la variable
- Mantiene la inmutabilidad por defecto
- Evita tener que inventar nombres como
x_str, x_num
fn main() {
let espacios = " "; // String
let espacios = espacios.len(); // usize - ¡diferente tipo!
println!("Número de espacios: {espacios}");
}
Funciones
Las funciones son bloques de código reutilizables. En Rust, el estilo es usar snake_case para nombres de funciones.
Función Básica
fn main() {
println!("¡Hola, mundo!");
otra_funcion();
}
fn otra_funcion() {
println!("Esta es otra función.");
}
Funciones con Parámetros
fn main() {
saludar("Alice", 25);
saludar("Bob", 30);
}
fn saludar(nombre: &str, edad: u32) {
println!("¡Hola {nombre}! Tienes {edad} años.");
}
Importante: En Rust, debes especificar el tipo de cada parámetro.
Funciones que Retornan Valores
fn main() {
let resultado = sumar(5, 3);
println!("5 + 3 = {resultado}");
let (suma, producto) = sumar_y_multiplicar(4, 7);
println!("4 + 7 = {suma}, 4 * 7 = {producto}");
}
fn sumar(a: i32, b: i32) -> i32 {
a + b // Expresión de retorno (sin punto y coma)
}
fn sumar_y_multiplicar(a: i32, b: i32) -> (i32, i32) {
(a + b, a * b) // Retornando una tupla
}
Conceptos importantes:
-> indica el tipo de retorno
- Las expresiones no llevan punto y coma
- Las declaraciones terminan con punto y coma
- La última expresión se retorna automáticamente
- También puedes usar
return explícitamente
Statements vs Expressions
fn main() {
// Statement (declaración) - no retorna un valor
let x = 5;
// Expression (expresión) - retorna un valor
let y = {
let x = 3;
x + 1 // Sin punto y coma - es una expresión
};
println!("El valor de y es: {y}"); // 4
// Esto sería un error porque las declaraciones no retornan valores:
// let x = (let y = 6); // ERROR!
}
Comentarios
Comentarios de Línea
fn main() {
// Este es un comentario de línea
println!("¡Hola!"); // También puedes comentar al final de la línea
}
Comentarios de Documentación
/// Esta función suma dos números.
///
/// # Ejemplos
///
/// ```
/// let resultado = sumar(2, 3);
/// assert_eq!(resultado, 5);
/// ```
fn sumar(a: i32, b: i32) -> i32 {
a + b
}
/// Esta estructura representa una persona.
///
/// # Campos
///
/// * `nombre` - El nombre de la persona
/// * `edad` - La edad de la persona
struct Persona {
nombre: String,
edad: u32,
}
Los comentarios /// generan documentación HTML con cargo doc.
Macros Básicos
println! - Imprimir en Consola
fn main() {
// Impresión básica
println!("¡Hola, mundo!");
// Con variables
let nombre = "Alice";
let edad = 30;
println!("Mi nombre es {nombre} y tengo {edad} años");
// Con posiciones
println!("Me llamo {0} y tengo {1} años. Sí, {0} es mi nombre.", nombre, edad);
// Con nombres
println!("Me llamo {nombre} y tengo {edad} años", nombre="Bob", edad=25);
// Con formato
println!("Pi es aproximadamente {:.2}", 3.14159); // 3.14
println!("En hexadecimal: {:x}", 255); // ff
println!("En binario: {:b}", 8); // 1000
}
print! vs println!
fn main() {
print!("Este texto ");
print!("está en ");
println!("la misma línea");
println!("Esta es una nueva línea");
}
eprintln! - Imprimir Errores
fn main() {
println!("Esto va a stdout");
eprintln!("Esto va a stderr");
}
Convenciones de Nombres
Rust tiene convenciones específicas para nombres:
snake_case
- Variables:
mi_variable
- Funciones:
mi_funcion
- Módulos:
mi_modulo
SCREAMING_SNAKE_CASE
- Constantes:
MI_CONSTANTE
- Valores estáticos:
MI_VALOR_ESTATICO
PascalCase
- Tipos (structs, enums):
MiStruct
- Traits:
MiTrait
// Ejemplos de convenciones
const PI: f64 = 3.14159;
static MAX_USUARIOS: u32 = 1000;
struct Usuario {
nombre_completo: String,
edad: u32,
}
fn crear_usuario(nombre: String, edad: u32) -> Usuario {
Usuario {
nombre_completo: nombre,
edad,
}
}
fn main() {
let nuevo_usuario = crear_usuario(String::from("Alice"), 30);
println!("Usuario creado: {}", nuevo_usuario.nombre_completo);
}
Ejercicios Prácticos
Ejercicio 1: Calculadora Básica
fn main() {
let a = 10;
let b = 3;
println!("{a} + {b} = {}", sumar(a, b));
println!("{a} - {b} = {}", restar(a, b));
println!("{a} * {b} = {}", multiplicar(a, b));
println!("{a} / {b} = {}", dividir(a, b));
}
fn sumar(a: i32, b: i32) -> i32 {
a + b
}
fn restar(a: i32, b: i32) -> i32 {
a - b
}
fn multiplicar(a: i32, b: i32) -> i32 {
a * b
}
fn dividir(a: i32, b: i32) -> f64 {
a as f64 / b as f64
}
Ejercicio 2: Temperatura
fn main() {
let celsius = 25.0;
let fahrenheit = celsius_a_fahrenheit(celsius);
println!("{celsius}°C = {fahrenheit:.1}°F");
let fahrenheit = 77.0;
let celsius = fahrenheit_a_celsius(fahrenheit);
println!("{fahrenheit}°F = {celsius:.1}°C");
}
fn celsius_a_fahrenheit(celsius: f64) -> f64 {
celsius * 9.0 / 5.0 + 32.0
}
fn fahrenheit_a_celsius(fahrenheit: f64) -> f64 {
(fahrenheit - 32.0) * 5.0 / 9.0
}
Puntos Clave para Recordar
- Las variables son inmutables por defecto - usa
mut cuando necesites mutabilidad
- Las funciones requieren tipos explícitos para parámetros y valores de retorno
- Las expresiones no llevan punto y coma al final si quieres que retornen un valor
- Usa snake_case para variables y funciones, PascalCase para tipos
- Los comentarios
/// generan documentación
println! es un macro, no una función (nota el !)