principiante30 minutosLección 4 de 10

Tipos de Datos

Domina los tipos escalares, tipos compuestos, inferencia de tipos y anotaciones de tipo explícitas en Rust

Tipos de Datos

Rust es un lenguaje estáticamente tipado — toda variable debe tener un tipo conocido en tiempo de compilación. El compilador es inteligente infiriendo tipos, pero siempre puedes anotarlos explícitamente.

Dos Categorías de Tipos

CategoríaDescripciónEjemplos
EscalarValor únicoenteros, floats, bool, char
CompuestoGrupo de valorestuplas, arrays, structs, enums

Tipos Escalares

Tipos Enteros

Rust ofrece enteros con signo (i) y sin signo (u) en varios tamaños:

TamañoCon SignoSin SignoRango (con signo)
8 bitsi8u8-128 a 127
16 bitsi16u16-32.768 a 32.767
32 bitsi32u32-2³¹ a 2³¹ -1
64 bitsi64u64-2⁶³ a 2⁶³ -1
128 bitsi128u128-2¹²⁷ a 2¹²⁷ -1
archisizeusizedepende de la plataforma (32/64 bits)
rust
fn main() { let a = 42; // i32 (por defecto) let b: u8 = 255; // Byte sin signo explícito let c = 100_000; // Separadores de guion bajo para legibilidad let d = 0xff; // Hexadecimal let e = 0o77; // Octal let f = 0b1111; // Binario let g = b'A'; // Literal de byte (u8, ASCII) // usize/isize para indexar colecciones let arr = [1, 2, 3]; let index: usize = 1; println!("{0}", &arr[index]); // 2 }
ℹ️Note

i32 es el tipo entero por defecto porque es rápido en CPUs modernas y evita problemas de desbordamiento comunes con tipos más pequeños.

Desbordamiento de Entero

rust
fn main() { let mut x: u8 = 255; // x = x + 1; // PANICA en modo debug (verificación de desbordamiento) // Wrapping explícito: let y = x.wrapping_add(1); // 0 (envuelve) let z = x.saturating_add(1); // 255 (satura en máximo) println!("wrapping: {y}, saturating: {z}"); }
MétodoComportamiento
wrapping_addEnvuelve (complemento a dos)
saturating_addSe detiene en valor mínimo/máximo
overflowing_addRetorna (resultado, desbordó bool)
checked_addRetorna Option (None en desbordamiento)
⚠️Warning

En modo debug, el desbordamiento de enteros paniquea. En modo release, envuelve silenciosamente. Nunca confíes en el comportamiento de desbordamiento — usa métodos explícitos.

Tipos de Punto Flotante

rust
fn main() { let x = 2.0; // f64 (por defecto, doble precisión) let y: f32 = 3.0; // f32 (precisión simple) // Operaciones f64 let al_cuadrado = x.powi(2); // 4.0 let raiz = x.sqrt(); // 1.414... let resto = 5.0 % 2.0; // 1.0 }
TipoPrecisiónTamañoCaso de Uso
f32~7 dígitos decimales4 bytesGráficos, GPUs
f64~15 dígitos decimales8 bytesComputación general
⚠️Warning

Los floats no implementan Eq u Ord — NaN y problemas de precisión hacen que la comparación no sea confiable. Usa f64::EPSILON para comparación aproximada:

rust
fn approx_eq(a: f64, b: f64) -> bool { (a - b).abs() < f64::EPSILON }

El Tipo Booleano

rust
fn main() { let rust_es_divertido = true; let es_dificil: bool = false; if rust_es_divertido { println!("¡Rust es divertido!"); } // Conversión a entero println!("{}", true as u8); // 1 println!("{}", false as u8); // 0 }

El Tipo Carácter

char tiene 4 bytes y representa un Valor Escalar Unicode:

rust
fn main() { let c = 'z'; let z: char = 'ℤ'; let gato_ojos_corazon = '😻'; println!("{c} {z} {gato_ojos_corazon}"); // char como número println!("{}", 'A' as u8); // 65 println!("{}", '😻' as u32); // 128571 }
ℹ️Note

char tiene 4 bytes (no 1 como el char de C). Soporta Unicode completo pero no es ASCII — usa u8 o &[u8] para datos a nivel de byte.

Tipos Compuestos

Tuplas

Las tuplas agrupan valores de tipos diferentes. Tamaño fijo, conocido en tiempo de compilación.

rust
fn main() { let tup: (i32, f64, char) = (500, 6.4, 'x'); // Desestructuración let (x, y, z) = tup; println!("{x}, {y}, {z}"); // Notación de punto (indexada desde 0) println!("{}", tup.0); // 500 println!("{}", tup.1); // 6.4 // Tupla unitaria (tupla vacía) let unit: () = (); }
PatrónEjemploCuándo Usar
Desestructurarlet (a, b) = tupExtraer todos los valores
Acceso por puntotup.0Extraer un valor
Ignorarlet (a, _, _) = tupExtraer algunos valores

Arrays

Los arrays tienen tamaño fijo, todos los elementos mismo tipo, almacenados en la stack:

rust
fn main() { let arr = [1, 2, 3, 4, 5]; // Anotación de tipo: [tipo; longitud] let tipado: [i32; 5] = [1, 2, 3, 4, 5]; // Expresión de repetición: [valor; cuenta] let ceros = [0; 10]; // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] // Acceso let primero = arr[0]; let segundo = arr[1]; // Verificación de límites en ejecución // let oops = arr[10]; // Panica: índice fuera de límites }
CaracterísticaArrayTupla
¿Mismo tipo?No
¿Tamaño fijo?
Tipo por defecto[T; N](T1, T2, ...)
Accesoarr[i] (verificado en ejecución)tup.i (tiempo de compilación)

Vectores (Avance)

Los vectores son arrays asignados en el heap, redimensionables:

rust
fn main() { let mut vec = vec![1, 2, 3]; vec.push(4); // Ahora [1, 2, 3, 4] println!("{vec:?}"); }

Inferencia de Tipos y Anotaciones

Rust infiere tipos en la mayoría de las situaciones:

rust
fn main() { // La inferencia funciona let x = 42; // i32 let y = 3.14; // f64 let cond = true; // bool // Las anotaciones aclaran intención let puerto: u16 = 8080; let pi: f32 = 3.14159; // A veces obligatorio (ambigüedad de tipo) let ambiguo = "hola".parse(); // ERROR: no puede inferir tipo let analizado: u32 = "42".parse().unwrap(); // OK con anotación // Sintaxis Turbofish let n = "100".parse::<i32>().unwrap(); }
Success
| Situación | Ejemplo | |-----------|---------| | `parse()` necesita anotación | `"42".parse::()` o `let x: i32 = "42".parse()?` | | Entero por defecto | `let x = 42` → `i32` | | Float por defecto | `let x = 3.14` → `f64` | | Genérico de colección | `Vec::new()` necesita contexto de tipo |

Alias de Tipo

rust
type Kilometros = i32; type Thunk = Box<dyn FnOnce() + Send>; fn main() { let distancia: Kilometros = 100; println!("{distancia} km"); }

Tipos Sized y Unsized

CategoríaEjemplos¿Tamaño en Tiempo de Compilación?
Sizedi32, f64, [i32; 5], String
Unsizedstr, [i32], dyn TraitNo (detrás de puntero)

Los tipos unsized deben estar siempre detrás de un puntero: &str, Box<dyn Trait>.

Preguntas de Práctica

  1. ¿Cuál es el tipo entero por defecto en Rust? ¿Y el tipo float?
  2. ¿Cuántos bytes tiene un char en Rust? ¿Cómo es diferente del char de C?
  3. ¿Cuál es la diferencia entre una tupla y un array?
  4. ¿Por qué no puedes comparar dos valores f64 con == directamente?
  5. ¿Qué sucede si accedes a un índice de array fuera de los límites?
  6. ¿Cuándo debes anotar el tipo de una variable explícitamente?
  7. ¿Para qué sirve el tipo unitario ()?
  8. ¿Cómo analizar una cadena en un entero?
  9. ¿Cuál es la diferencia entre usize y u64?
  10. ¿Qué métodos puedes usar para manejar el desbordamiento de enteros de forma segura?
Progreso40%