6  Tipos de datos compuestos

En la Sección 5.2 ya hemos visto los tipos básicos de datos: reales, enteros, caracteres, booleanos y unitario. Sin embargo, con frecuencia necesitaremos agrupar varios valores para representar una entidad o manejar colecciones de datos. Para ello, Rust define dos tipos de datos compuestos: las tuplas y los arrays.

6.1 Tuplas

Una tupla es una colección ordenada de valores. Sus elementos pueden tener tipos distintos y su longitud no puede cambiar una vez definida. Se escriben entre paréntesis, separados por comas.

fn main() {
    let persona = ("Ana", 25, true);

    println!("Contenido de la tupla:");
    println!("\t{:?}", persona);

    println!("Elementos de la tupla:");
    println!("\tNombre: {}", persona.0);
    println!("\tEdad: {}", persona.1);
    println!("\tActivo: {}", persona.2);
}
  • La variable persona contiene una tupla con tres elementos: una cadena ("Ana"), un entero (25) y un booleano (true).

  • La sentencia

    println!("\t{:?}", persona);

    muestra el contenido completo de la tupla. El especificador :? indica que el valor debe imprimirse usando su representación de depuración (debug).

  • Los elementos de una tupla se acceden mediante un punto seguido de su posición. El primer elemento es persona.0, el segundo persona.1 y el tercero persona.2.

  • La posición ha de ser un literal numérico (un número tal cual). No puede ser una variable ni una expresión. El índice identifica un campo concreto y debe conocerse en compilación

  • Las últimas sentencias muestran cada elemento por separado:

    println!("\tNombre: {}", persona.0);
    println!("\tEdad: {}", persona.1);
    println!("\tActivo: {}", persona.2);

    El especificador {} indica dónde debe insertarse el valor que aparece como segundo argumento de println!.

  • La secuencia \t representa un carácter de tabulación y se utiliza aquí para mejorar la presentación de la salida.

6.2 La tupla vacía

La tupla vacía se escribe () y no contiene ningún elemento. Como vimos en la Sección 5.2.2 también se le llama valor unitario y es de tipo unitario.

fn main() {
    let tupla_vacia = ();
    println!("{:?}", tupla_vacia);
}

Aunque pueda parecer poco útil, la tupla vacía tiene un papel importante en Rust. Por ejemplo, es el valor que devuelven muchas funciones cuya finalidad es realizar una acción y no producir un resultado.

6.3 Arrays

Al igual que una tupla, un array es una colección ordenada de valores cuya longitud no puede cambiar una vez definido. Pero a diferencia de las tuplas, sus elementos han de ser todos del mismo tipo.

Los elementos de un array se escriben entre corchetes, separados por comas. También se usan los corchetes para acceder a los elementos por su índice. Al igual que en C y muchos otros lenguajes, el primer elemento es el 0, el segundo es el 1, etc. El índice puede ser un literal numérico, una variable o una expresión.

fn main() {
    let numeros = [10, 20, 30, 40, 50];

    println!("Contenido del array:");
    println!("\t{:?}", numeros);
  
    println!("Elementos del array:");
    println!("\tPrimero: {}", numeros[0]);
    println!("\tSegundo: {}", numeros[1]);
    println!("\tTercero: {}", numeros[2]);
}

La variable numeros contiene un array con cinco elementos. Todos ellos son enteros. Si alguno no lo fuera, el compilador mostraría un error. Como en el siguiente ejemplo:

fn main() {
    let datos = [10, 20, "azul"]; // ¡ERROR! Los tipos han de ser iguales.

    println!("Contenido del array:");
    println!("\t{:?}", datos);
}

También es posible definir un array indicando su tamaño y el valor que tendrán todos sus elementos. Para ello se escribe el valor inicial, seguido de un punto y coma y del número de elementos:

let ceros = [0; 5];

Esto significa que se debe repetir el valor 0 cinco veces. El array ceros contiene cinco elementos, todos ellos iguales a 0. El tipo de los elementos es i32, que es el tipo que Rust deduce para el literal 0. Esta forma de definir arrays resulta especialmente útil cuando todos sus elementos han de comenzar con el mismo valor. Por ejemplo:

fn main() {
    let ceros = [0; 5];

    println!("Contenido del array:");
    println!("\t{:?}", ceros);
}

También se puede indicar explícitamente el tipo de los elementos:

let valores: [f64; 4] = [0.0; 4];

Aquí valores es un array de cuatro elementos de tipo f64, todos ellos inicializados a 0.0.

La forma [valor; tamaño] requiere que el valor pueda copiarse para inicializar los elementos. En este capítulo trabajaremos con tipos sencillos, como i32, f64 o bool, que cumplen este requisito. En la Sección 9.5 veremos que no todos los tipos de Rust implementan el trait Copy. Con algunos de ellos no será posible utilizar esta forma de inicialización, aunque sí se podrán definir arrays escribiendo sus elementos uno a uno.

En cualquier caso, el tamaño siempre forma parte del tipo del array. Por tanto, [i32; 5] y [i32; 10] son tipos distintos, aunque sus elementos sean del mismo tipo.

6.4 Comparación entre tuplas y arrays

La Tabla 6.1 resume las similitudes y diferencias entre tuplas y arrays.

Tabla 6.1: Comparación entre tuplas y arrays
Característica Tupla Array
Longitud fija
Elementos del mismo tipo No necesariamente
Delimitadores Paréntesis () Corchetes []
Acceso a elementos Punto (.0, .1, …) Índice ([0], [1], …)
Acceso mediante vble o expr. No
Ejemplo ("TCP", 443, true) [10, 20, 30, 40, 50]

6.5 Resumen

En este capítulo hemos presentado los dos tipos de datos compuestos básicos que proporciona Rust: las tuplas y los arrays.

Las ideas principales son las siguientes:

  • Una tupla agrupa un número fijo de valores que pueden ser de tipos diferentes.
  • Los elementos de una tupla se acceden mediante su posición, utilizando la notación .0, .1, .2, etc.
  • La tupla vacía () representa el valor unitario y suele utilizarse como valor de retorno de funciones que realizan una acción pero no producen un resultado.
  • Un array almacena un número fijo de elementos del mismo tipo.
  • Los elementos de un array se acceden mediante un índice entre corchetes, comenzando por el índice 0.
  • Tanto las tuplas como los arrays tienen un tamaño fijo, pero las tuplas permiten mezclar tipos mientras que los arrays no.
  • En los arrays se pueden usar variables o expresiones para los índices. En las tuplas, no. Solo constantes numéricas literales.

En los próximos capítulos estudiaremos nuevos tipos de datos que permiten representar estructuras más complejas y colecciones de tamaño variable.