Lección 02 de 26 Primeros pasos
Variables y mutabilidad
- Una variable es una caja con nombre. let puntos = 10; guarda un 10 en la caja puntos.
- Por defecto es inmutable: la caja está cerrada con candado. puntos = 20; no compila.
- Con mut quitas el candado: let mut puntos = 10; permite cambiar el valor. Ahora puntos = 20; funciona.
- Shadowing: let x = x + 1; crea una caja nueva con el mismo nombre que tapa a la anterior.
Una variable es un nombre que le das a un valor para poder usarlo después. En Rust se declaran con let y tienen una particularidad que sorprende al principio: por defecto no se pueden modificar.
No es un capricho. Si un valor no cambia, es más fácil razonar sobre el código y el compilador puede garantizar que nadie lo modifica a tus espaldas. Cuando necesites que cambie, lo dirás de forma explícita.
Declarar variables con let
fn main() {
let nombre = "Ferris";
let edad = 7;
println!("{nombre} tiene {edad} años");
}No hace falta indicar el tipo: Rust lo infiere a partir del valor. Si quieres, puedes escribirlo tú después del nombre, separado por dos puntos:
fn main() {
let edad: i32 = 7;
println!("Edad: {edad}");
}Verás los tipos con detalle en la siguiente lección.
Nombres: Las variables se escriben en
snake_case: minúsculas y palabras separadas por guiones bajos, comopuntos_totales. Si usascamelCase, el compilador te avisará.
Inmutables por defecto
Intenta cambiar el valor de una variable normal y el compilador se negará. Pulsa Ejecutar para ver el error:
fn main() {
let puntos = 10;
println!("Puntos: {puntos}");
puntos = 20;
println!("Puntos: {puntos}");
}error[E0384]: cannot assign twice to immutable variable `puntos`
--> src/main.rs:4:5
|
2 | let puntos = 10;
| ------ first assignment to `puntos`
3 | println!("Puntos: {puntos}");
4 | puntos = 20;
| ^^^^^^^^^^^ cannot assign twice to immutable variable
|
help: consider making this binding mutable
|
2 | let mut puntos = 10;
| +++Fíjate en el help: del final: el propio compilador te dice cómo arreglarlo.
Variables mutables con mut
Añade mut delante del nombre y la variable podrá cambiar de valor:
fn main() {
let mut puntos = 10;
println!("Puntos: {puntos}");
puntos = 20;
println!("Puntos: {puntos}");
puntos += 5;
println!("Puntos: {puntos}");
}puntos += 5 es la forma corta de escribir puntos = puntos + 5. También existen -=, *=, /= y %=.
Ten en cuenta dos cosas:
mutpermite cambiar el valor, pero no el tipo: si la variable guarda un número, siempre guardará un número.- Si marcas una variable como
muty nunca la modificas, el compilador te avisa con un warning:variable does not need to be mutable. Usamutsolo cuando lo necesites.
Constantes
Para valores fijos que no cambian nunca, como un límite o un dato de configuración, usa const:
const SEGUNDOS_POR_HORA: u32 = 60 * 60;
const MAX_VIDAS: u32 = 3;
fn main() {
println!("Una hora tiene {SEGUNDOS_POR_HORA} segundos");
println!("Empiezas con {MAX_VIDAS} vidas");
}Las constantes se diferencian de las variables en varias cosas:
- Siempre son inmutables: no admiten
mut. - El tipo es obligatorio: no se infiere.
- Se escriben en
MAYÚSCULAS_CON_GUIONES_BAJOS. - Pueden declararse fuera de cualquier función, a nivel global.
- Su valor tiene que poder calcularse al compilar, como
60 * 60.
Legibilidad: Puedes usar
_para separar las cifras de un número largo:1_000_000es lo mismo que1000000, pero se lee mucho mejor.
Shadowing: redeclarar una variable
Puedes declarar una variable nueva con el mismo nombre que otra anterior. La nueva oculta a la anterior (en inglés, shadowing):
fn main() {
let x = 5;
let x = x + 1;
let x = x * 2;
println!("x vale {x}");
}Cada let crea una variable nueva que se calcula a partir de la anterior. El resultado es 12 y x sigue siendo inmutable: nadie puede cambiarla por error más abajo.
La gran ventaja del shadowing es que la variable nueva puede tener otro tipo. Es muy útil cuando transformas un valor y no quieres inventar nombres como espacios_texto y espacios_numero:
fn main() {
let espacios = " ";
let espacios = espacios.len();
println!("Hay {espacios} espacios");
}.len() devuelve la longitud del texto, así que espacios pasa de ser un texto a ser un número. Con mut esto no se puede hacer, porque el tipo no puede cambiar:
fn main() {
let mut espacios = " ";
espacios = espacios.len();
println!("Hay {espacios} espacios");
}error[E0308]: mismatched types
--> src/main.rs:3:16
|
2 | let mut espacios = " ";
| ------ expected due to this value
3 | espacios = espacios.len();
| ^^^^^^^^^^^^^^ expected `&str`, found `usize`Resumiendo las tres formas de dar nombre a un valor:
let | let mut | const | |
|---|---|---|---|
| ¿Puede cambiar de valor? | No | Sí | No |
| ¿Tipo obligatorio? | No | No | Sí |
| ¿Fuera de funciones? | No | No | Sí |
| Nombre | snake_case | snake_case | MAYÚSCULAS |
Ámbito de bloque
Una variable solo existe dentro del bloque { } en el que se declara, desde su let hasta la llave de cierre. Ese tramo es su ámbito (scope):
fn main() {
let x = 5;
{
let x = x * 10;
let y = 1;
println!("Dentro: x = {x}, y = {y}");
}
println!("Fuera: x = {x}");
}Dentro del bloque, el shadowing crea una x que vale 50. Al cerrar la llave esa x desaparece y vuelve a verse la original, que vale 5.
Lo mismo le pasa a y: al salir del bloque deja de existir. Si intentas usar una variable fuera de su ámbito, el código no compila:
fn main() {
{
let secreto = 42;
println!("Dentro: {secreto}");
}
println!("Fuera: {secreto}");
}error[E0425]: cannot find value `secreto` in this scopeConsejo: Si declaras una variable y no la usas, el compilador te avisa. Si es a propósito, empieza su nombre por guion bajo, como
_temporal, y el aviso desaparece.
Hora de practicar
Ejercicios
0/4 resueltos
Reto 1 de 4 Resuelto
vidas es inmutable. Arréglalo sin tocar la línea vidas -= 1; para que imprima Vidas: 3 y después Vidas: 2.Vidas: 3 Vidas: 2
Al ejecutar, unos tests ocultos comprueban tus funciones.
Pulsa Ejecutar para compilar y ver aquí la salida de tu programa.