Saltar al contenido principal

Variables y Mutabilidad

  • Se declaran utilizando let o const seguido del tipo de dato
  • Si no se especifica el tipo, el compilador los puede inferir y asignar los tipos por defecto, como i32 para los enteros
  • Son inmutables por defecto
// let
let x = 10; // default: i32
let y: u8 = 5;

// const
const PI: f32 = 3.1416;
const CODE: &str = "123";
  • No se pueden leer o manipular variables no inicializadas
  • Pero, si se pueden declarar e inicializar después, entonces ya se podrá leer o manipular la variable
// Error
let x: i32;
println!("Num: {x}");

// Correct
let y: i32;
y = 10;
prinln!("Num: {y}");

let

Inmutable

  • Las variables, cuando se crean o inicializan, son inmutables por defecto
fn main() {
let name: &str = "Rust";
let x: u8 = 5;
println!("El valor de x: {x}");

x = 6; // Error, no se puede reasignar el valor de una variable
println!("El nuevo valor de x: {x}"); }
}

Mutable

  • Agregando mut al crear la variable
  • Al agregar mut también transmite la intención a los futuros lectores del código al indicar que otras partes del código cambiarán el valor de esta variable
fn main() {
    let mut num: u16 = 10; // mut para poder modificarla
    println!("El numero es: {}", num);

    num = 20; // Se puede reasignar
    println!("El numero ahora es: {}", num);
}

const

  • Constantes en tiempo de compilación
  • Se debe especificar el tipo de dato
  • Similar a las variables inmutables
  • No se puede utilizar mut para hacerlas mutables
  • Los valores de la variable no pueden cambiar y siempre son inmutables
  • TODO_MAYUSCULA_CON_GUION_BAJO
  • Se puede declarar en cualquier ámbito (scope global o local)
  • Las constantes son válidas durante todo el tiempo que se ejecuta el programa, dentro del ámbito en que se declararon
const LIMIT: i32 = 20;
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
const CODE: &str = "123";

//const mut z: i32 = 20; // Error

Diferencia entre const y let

  • const no es solo una variable inmutable, sino una constante en tiempo de compilación
  • const no puede depender de cálculos en tiempo de ejecución ni de entrada del usuario
  • let se define en tiempo de ejecución
  • let puede cambiar dependiendo de la lógica del programa
  • const X: i32 = 5; debe ser conocido en tiempo de compilación, lo que lo hace más restrictivo.
  • let y = 5; (sin mut) es inmutable, pero su valor puede determinarse en tiempo de ejecución.

Si se hiciera const obligatorio para inmutabilidad, se perdería la flexibilidad de valores inmutables determinados en tiempo de ejecución.

const MAX_USERS: u32 = 100; // Esto es fijo, conocido en compilación
const NAME: &str = "Rust";

let user_id = get_user_id(); // Se obtiene en tiempo de ejecución

Shadowing

  • Se puede declarar una variable con el mismo nombre de una variable anterior
  • La primera variable se sobreescribirá por la segunda
  • Se debe escribir let (como se declaran las variables normalmente) ya que sino entonces únicamente se estaría actualizando o usando el valor de la variable
  • Se puede crear ámbitos donde se cambia el valor pero no es de forma global o general
fn main() {
let x = 5;
let x = x + 1; // Shadowing

{
let x = x * 2; // Shadowing interno
println!("X en el scope interno: {x}"); //12
}

println!("El valor de X: {x}"); // 6
}
  • Se puede cambiar el tipo de dato de la variable ya que se está creando una nueva variable
fn main() {
// Simulando entrada del usuario con espacios extra
let input = " 42.5 ";
println!("Valor original: '{}'", input); // ' 42.5 '

// Shadowing 1: String -> String (trim)
let input = input.trim();
println!("Shadowing 1: String = '{}'", input); // '42.5'

// Shadowing 2: String -> f64 (parse)
let input: f64 = input.parse().expect("No es un número válido");
println!("Shadowing 2: f64 = {}", input); // 42.5

// Shadowing 3: f64 -> i32 (round y conversión)
let input = input.round() as i32;
println!("Shadowing 3: i32 = {}", input); // 42

// Shadowing 4: i32 -> String (formateo)
let input = format!("El resultado es: {}", input);
println!("Shadowing 4: String = {}", input); // "El resultado es: 42"
}

fn other_example() {
let number = "T-H-R-E-E";// "number" es de tipo "str"
println!("Spell a number: {number}");

// Using variable shadowing
let number = 3; // "number" ahora es de tipo i32
println!("Number plus two is: {}", number + 2);
}

Diferencia con mut

  • Se puede declarar una variable con el mismo nombre que una variable anterior
  • Se puede cambiar el tipo de dato de la variable ya que se está creando una nueva variable
  • La primera variable queda sombreada o sobreescrita por la segunda
  • No es lo mismo que agregar mut a la variable (que actualizaría el valor de la variable)
  • mut no permite el cambio de tipo de variable
let x: i32 = 10;

//x = 20; // Mutable, si tuviera la palabra mut
let x: i32 = x * 2; // Shadowing
fn main() {
    let x = 5;
    let x = x + 1; // 6

    {
        // Ya que es un nuevo scope, al salir de el se pierde la variable x
        let x = x * 2; // 6 * 2 = 12
        println!("El valor de x en el scope interno es: {x}");
    }

    println!("El valor de x en la función principal es: {x}"); // 6
}