Tipos de datos

From El Mago del Rust
Jump to navigation Jump to search

Todo valor en Rust es de un tipo específico. Rust necesita saber ese tipo al momento de compilar el programa, y muchas veces puede inferir el tipo. En otras ocasiones debemos indicarlo explícitamente, y si no lo hacemos produciremos un error de compilación.

Hay dos categorías:

Escalares[edit]

Un tipo escalar representa un valor único, y existen cuatro tipos escalares: enteros, punto flotantes, booleanos, y caracteres.

Enteros[edit]

Un entero es un número sin una parte fraccional. Pueden tener signo o no, y ser de 8 a 128 bits. Si tiene signos, el tipo empieza con i y si no tiene signo empieza con u.

Tamaños y signo[edit]

El tipo empieza con i y u, y continúa con el tamaño en bits, desde 8 hasta 128 bits: i8, u8, i16, u16, y así sucesivamente hasta i128 y u128.

Se usa complemento a dos para representar los números negativos.


Punto flotante[edit]

Hay dos tipos primitivos de punto flotante: f32 (32 bits, precisión simple) y f64 (64 bits, doble precisión). Si no se especifica el tipo, se asume por defecto que es f64. Se usa el estándar IEEE-754 de punto flotante.

fn main(){
    let pi = 3.14; // se asume tipo f64
    let as: f32 = 3.14; // esta variable es de tipo f32
}

Booleanos[edit]

Una variable booleana puede ser o true o false, y ocupa un byte de almacenamiento.

fn main(){
    let bandera = true;
    let otra_bandera: bool = false;
}

Caracteres[edit]

Para almacenar letras, se puede usar char, y se define con comilla única (no doble comilla o comillas que son para las cadenas de caracteres). Los caracteres ocupan 4 bytes de almacenamiento.

fn main(){
    let p = 'p';
}

Compuestos[edit]

Los tipos compuestos agrupan varios valores en un sólo tipo. Existen dos: tuples y arreglos.

Tuples[edit]

Agrupan diferentes tipos en un sólo tipo compuesto. Tienen un tamaño definido: no se pueden modificar de tamaño una vez creados.

Se crean usando paréntesis y separando la lista de valores con comas. Cada posición es un tipo, y no necesitan ser iguales.

fn main(){
     let tuple: (i32, char) = (12,'d');
     println!("El valor es {}",tuple.1);
}

La salida del programa sería d. (tuple.0 se refiere al i32, y tuple.1 al caracter).

Otra forma es asignar parte de ese tuple a una variable, de la siguiente manera:

fn main(){
     let tuple: (i32, char) = (12,'d');
     let (a,b)=tuple;
     println!("El valor es {}",b);
}

Arreglos[edit]

En los arreglos, todos los valores deben ser del mismo tipo, y deben tener un tamaño fijo. En este ejemplo, el arreglo a tiene 3 valores:

fn main(){
    let a = [0,1,2];
}

Los arreglos se definen estáticamente, por lo que están en el stack y no en el heap. Si se desea algo dinámico, se puede usar un vector.

Si se desea explicitar el tipo y el número de elemento se haría:

fn main(){
    let a: [i32;3] = [0,1,2];
}

Si quieres inicializar todo un arreglo con un mismo valor puedes hacer esto:

fn main(){
    let a = [5;10]; //esto inicializa con 10 elementos con un valor de 5.
}

Los elementos de un arreglo se accesan de la misma forma que en otros lenguajes: a[0] accesa su primer elemento, y así sucesivamente.

Arreglos bidimensionales[edit]

Se pueden definir explícitamente:

fn main(){
     let mut state = [[0,0],[0,1]];
}

También se pueden definir de esta forma, un arreglo de 8 por 16:

fn main(){
    let mut arreglo: [[i32; 16]; 8];
}

String[edit]

Un String está localizado en el heap y no en el stack, por lo que puede cambiar de tamaño durante la ejecución del programa. Se crea de la siguiente forma:

let s = String::from("hola");

Y se puede extender si le hacemos un push:

fn main() {
    let mut s = String::from("hola");
    s.push_str(", ¿cómo te va?"); 
    println!("{}", s); 
}

Este programa creará el String y le agregará al final el mensaje ", ¿cómo te va?".