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 !)
Próximo Paso
Ahora que conoces la sintaxis básica, en el siguiente capítulo exploraremos los tipos de datos de Rust en detalle, incluyendo enteros, flotantes, booleanos, caracteres, tuplas y arrays.