Rust
Tutorial Rust: Arrays y strings
Aprende en Rust a manejar arrays, slices y cadenas de texto (&str y String) con ejemplos claros y seguridad en memoria.
Aprende Rust y certifícateArrays y slices
En Rust, los arrays son colecciones de elementos del mismo tipo con un tamaño fijo conocido en tiempo de compilación. A diferencia de otros lenguajes, los arrays en Rust son una estructura de datos fundamental que ofrece un rendimiento predecible y seguridad en el acceso a memoria.
Arrays de tamaño fijo
Un array en Rust se declara especificando tanto el tipo de los elementos como su cantidad. La sintaxis básica es [T; N]
, donde T
es el tipo de los elementos y N
es el número de elementos.
// Array de 5 enteros
let numeros: [i32; 5] = [1, 2, 3, 4, 5];
// Array de 3 booleanos
let banderas: [bool; 3] = [true, false, true];
// Array de caracteres
let vocales: [char; 5] = ['a', 'e', 'i', 'o', 'u'];
Rust también ofrece una forma concisa de crear arrays con el mismo valor repetido:
// Array de 10 ceros
let ceros = [0; 10]; // Equivalente a [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
// Array de 5 cadenas vacías
let cadenas_vacias = [""; 5];
Acceso a elementos
Para acceder a los elementos de un array, utilizamos la notación de índice con corchetes. Los índices en Rust comienzan en 0:
let colores = ["rojo", "verde", "azul"];
let primer_color = colores[0]; // "rojo"
let segundo_color = colores[1]; // "verde"
Una característica importante de Rust es que realiza comprobaciones de límites en tiempo de ejecución:
let numeros = [1, 2, 3];
// let valor = numeros[5]; // ¡Error en tiempo de ejecución!
// thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 5'
Iteración sobre arrays
Podemos recorrer los elementos de un array de varias formas:
let notas = [7, 8, 9, 6, 10];
// Usando for
for nota in notas {
println!("Nota: {}", nota);
}
// Usando for con índices
for i in 0..notas.len() {
println!("Nota {}: {}", i + 1, notas[i]);
}
// Usando iter() para obtener referencias inmutables
for nota in notas.iter() {
println!("Referencia a nota: {}", nota);
}
Métodos útiles para arrays
Los arrays tienen varios métodos útiles disponibles:
let valores = [10, 20, 30, 40, 50];
// Obtener la longitud
let longitud = valores.len(); // 5
// Comprobar si está vacío
let esta_vacio = valores.is_empty(); // false
// Obtener una referencia al primer elemento
if let Some(primero) = valores.first() {
println!("Primer valor: {}", primero);
}
// Obtener una referencia al último elemento
if let Some(ultimo) = valores.last() {
println!("Último valor: {}", ultimo);
}
// Comprobar si contiene un valor
let contiene_30 = valores.contains(&30); // true
Mutabilidad en arrays
Por defecto, los arrays son inmutables en Rust. Para crear un array mutable:
let mut puntuaciones = [0, 0, 0, 0];
puntuaciones[0] = 10;
puntuaciones[1] = 20;
println!("Puntuaciones: {:?}", puntuaciones); // [10, 20, 0, 0]
Slices: vistas de arrays
Los slices son una vista o "referencia" a una sección de un array. No poseen los datos, sino que "apuntan" a ellos. La sintaxis de un slice es &[T]
, donde T
es el tipo de los elementos.
let numeros = [1, 2, 3, 4, 5];
// Slice que referencia a todo el array
let todos = &numeros[..];
// Slice que referencia a los primeros tres elementos
let primeros_tres = &numeros[0..3]; // [1, 2, 3]
// Slice que referencia a los últimos dos elementos
let ultimos_dos = &numeros[3..5]; // [4, 5]
// Formas abreviadas
let desde_inicio = &numeros[..3]; // Equivalente a [0..3]
let hasta_final = &numeros[2..]; // Equivalente a [2..5]
Los slices son especialmente útiles para pasar secciones de arrays a funciones sin necesidad de copiar los datos:
fn suma_slice(numeros: &[i32]) -> i32 {
let mut total = 0;
for &n in numeros {
total += n;
}
total
}
let valores = [10, 20, 30, 40, 50];
let total = suma_slice(&valores[1..4]); // Suma 20+30+40=90
println!("La suma es: {}", total);
Slices de dos dimensiones
También podemos crear slices de arrays multidimensionales:
let matriz = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
// Slice de la primera fila
let primera_fila = &matriz[0]; // &[1, 2, 3]
// Slice de las dos primeras filas
let dos_filas = &matriz[0..2]; // &[[1, 2, 3], [4, 5, 6]]
Seguridad y rendimiento
Los arrays y slices en Rust ofrecen varias ventajas:
- Rendimiento predecible: Al tener tamaño fijo, los arrays se almacenan completamente en la pila (stack), lo que proporciona acceso rápido.
- Seguridad de memoria: Rust verifica los límites de los arrays en tiempo de ejecución, evitando accesos fuera de rango.
- Eficiencia en slices: Los slices permiten trabajar con secciones de datos sin copiarlos, solo referenciándolos.
fn procesar_datos(datos: &[f64]) {
// Trabajar con una referencia a los datos sin copiarlos
let promedio = datos.iter().sum::<f64>() / datos.len() as f64;
println!("Promedio: {:.2}", promedio);
}
let mediciones = [23.5, 24.0, 22.8, 23.2, 24.1];
procesar_datos(&mediciones); // Pasamos una referencia a todo el array
procesar_datos(&mediciones[2..]); // Pasamos solo las últimas mediciones
Los arrays y slices forman la base para estructuras de datos más complejas en Rust, como los vectores (Vec<T>
), que veremos en lecciones posteriores sobre colecciones estándar.
&str vs String
En Rust, trabajar con texto implica entender dos tipos fundamentales: &str
y String
. Estos tipos representan cadenas de texto pero con características y propósitos diferentes que resultan de la filosofía de Rust sobre seguridad y rendimiento.
Cadenas literales (&str)
El tipo &str
(pronunciado "string slice" o "rebanada de cadena") es la forma más básica de representar texto en Rust. Se trata de una referencia inmutable a una secuencia de caracteres UTF-8 almacenada en algún lugar de la memoria.
// Cadena literal asignada a una variable
let saludo: &str = "Hola, mundo";
// El tipo &str se infiere automáticamente para literales de cadena
let nombre = "Rust";
Las características principales de &str
son:
- Inmutabilidad: No puedes modificar su contenido
- Tamaño fijo: Conocido en tiempo de compilación
- Eficiencia: No requiere asignación de memoria en el heap
- Referencia: No posee los datos, solo los "observa"
Las cadenas literales como "Hola"
están almacenadas en el binario del programa y tienen una duración igual a la del programa completo.
Cadenas dinámicas (String)
El tipo String
es una estructura de datos que posee una secuencia de caracteres UTF-8 y la almacena en el heap, permitiendo que su contenido sea modificable y su tamaño pueda cambiar durante la ejecución.
// Crear una String vacía
let mut mensaje = String::new();
// Crear una String a partir de un literal
let mut nombre = String::from("Ana");
// Otra forma de crear una String
let apellido = "García".to_string();
Las características principales de String
son:
- Mutabilidad: Puedes modificar su contenido
- Tamaño dinámico: Puede crecer o reducirse en tiempo de ejecución
- Propiedad: Posee sus datos y los gestiona
- Asignación en heap: Requiere memoria del montículo (heap)
Operaciones básicas con strings
Concatenación
Con String
podemos añadir contenido de varias formas:
let mut saludo = String::from("Hola");
// Añadir un &str con push_str()
saludo.push_str(", ");
// Añadir un solo carácter con push()
saludo.push('R');
saludo.push('u');
saludo.push('s');
saludo.push('t');
println!("{}", saludo); // "Hola, Rust"
// Concatenar con el operador +
let nombre = String::from("Programación ");
let lenguaje = "Rust";
// Nota: + toma propiedad del primer argumento
let curso = nombre + "en " + lenguaje;
println!("{}", curso); // "Programación en Rust"
Para concatenaciones más complejas, el macro format!
es más eficiente y legible:
let nombre = "Ana";
let edad = 28;
let presentacion = format!("Me llamo {} y tengo {} años", nombre, edad);
println!("{}", presentacion); // "Me llamo Ana y tengo 28 años"
Búsqueda y comprobación
Podemos buscar texto dentro de cadenas:
let frase = "Rust es un lenguaje seguro y eficiente";
// Comprobar si contiene una subcadena
let contiene_seguro = frase.contains("seguro"); // true
// Comprobar si comienza con una subcadena
let comienza_con_rust = frase.starts_with("Rust"); // true
// Comprobar si termina con una subcadena
let termina_con_rapido = frase.ends_with("rápido"); // false
Reemplazo y transformación
let codigo = "let x = 10;";
// Reemplazar texto
let codigo_nuevo = codigo.replace("10", "42");
println!("{}", codigo_nuevo); // "let x = 42;"
// Convertir a mayúsculas/minúsculas
let texto = "Rust";
println!("{}", texto.to_uppercase()); // "RUST"
println!("{}", texto.to_lowercase()); // "rust"
Conversión entre &str y String
Es común necesitar convertir entre estos dos tipos:
// De &str a String
let literal = "Hola";
let string_objeto = literal.to_string(); // o String::from(literal)
// De String a &str (préstamo)
let objeto = String::from("Mundo");
let slice: &str = &objeto;
// También funciona automáticamente cuando se pasa como argumento
Indexación y rebanado
A diferencia de otros lenguajes, Rust no permite indexar strings directamente con [i]
debido a que los caracteres UTF-8 pueden ocupar múltiples bytes:
let texto = "Rust 🦀";
// let primer_caracter = texto[0]; // ¡ERROR! No se puede indexar por byte
// En su lugar, podemos iterar sobre caracteres
for c in texto.chars() {
println!("{}", c);
}
// O convertir a un vector de caracteres
let caracteres: Vec<char> = texto.chars().collect();
let primer_caracter = caracteres[0]; // 'R'
Para obtener rebanadas (slices) de strings, debemos asegurarnos de cortar en límites válidos de caracteres UTF-8:
let mensaje = "Hola Rust";
let hola = &mensaje[0..4]; // "Hola"
let rust = &mensaje[5..]; // "Rust"
// Cuidado: esto causaría pánico si cortamos a mitad de un carácter multibyte
// let emoji_incorrecto = "🦀"[0..1]; // ¡ERROR en tiempo de ejecución!
Cuándo usar cada tipo
Usa &str cuando:
Necesites una referencia inmutable a texto
Quieras pasar texto como parámetro sin transferir propiedad
Trabajes con texto de solo lectura
Usa String cuando:
Necesites modificar el contenido del texto
Debas construir texto dinámicamente
Requieras ser propietario del texto
// Función que acepta una referencia a str (más flexible)
fn saludar(nombre: &str) {
println!("¡Hola, {}!", nombre);
}
// Podemos llamarla con &str o con String
let nombre_literal = "Carlos";
let nombre_string = String::from("Ana");
saludar(nombre_literal);
saludar(&nombre_string); // Coerción automática de &String a &str
Recuerda que String
es propietario de su contenido mientras que &str
generalmente es una referencia. Esta distinción es fundamental para entender cómo Rust gestiona la memoria y garantiza la seguridad sin necesidad de un recolector de basura.
Otras lecciones de Rust
Accede a todas las lecciones de Rust y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Rust
Introducción Y Entorno
Primer Programa
Introducción Y Entorno
Instalación Del Entorno
Introducción Y Entorno
Funciones
Sintaxis
Operadores
Sintaxis
Estructuras De Control Condicional
Sintaxis
Arrays Y Strings
Sintaxis
Manejo De Errores Panic
Sintaxis
Variables Y Tipos Básicos
Sintaxis
Estructuras De Control Iterativo
Sintaxis
Colecciones Estándar
Estructuras De Datos
Option Y Result
Estructuras De Datos
Pattern Matching
Estructuras De Datos
Estructuras (Structs)
Estructuras De Datos
Enumeraciones Enums
Estructuras De Datos
El Concepto De Ownership
Ownership
Lifetimes Básicos
Ownership
Slices Y Referencias Parciales
Ownership
References Y Borrowing
Ownership
Funciones Anónimas Closures
Abstracción
Traits De La Biblioteca Estándar
Abstracción
Traits
Abstracción
Generics
Abstracción
Channels Y Paso De Mensajes
Concurrencia
Memoria Compartida Segura
Concurrencia
Threads Y Sincronización Básica
Concurrencia
Introducción A Tokio
Asincronía
Fundamentos Asíncronos Y Futures
Asincronía
Async/await
Asincronía
Ejercicios de programación de Rust
Evalúa tus conocimientos de esta lección Arrays y strings con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender la declaración y uso de arrays de tamaño fijo en Rust.
- Aprender a trabajar con slices como vistas de arrays para manipular secciones sin copiar datos.
- Diferenciar entre los tipos de cadenas &str y String, y cuándo usar cada uno.
- Realizar operaciones básicas con strings, incluyendo concatenación, búsqueda y transformación.
- Entender la seguridad y rendimiento que Rust ofrece en el manejo de arrays, slices y cadenas de texto.