Saltar al contenido principal

Escalares

  • Scalar Data Types
  • Rust tiene cuatro tipos escalares principales: números enteros, números de punto flotante, booleanos, y caracteres
  • Un tipo escalar representa un valor único, 1, 2.5, true y a
  • Es posible no escribir el tipo de dato pero es recomendable hacerlo como buena práctica

Integer

  • Rust tiene dos tipos de enteros: signed (+ positivos y - negativos) y unsigned (solo + positivos)
  • Si no se especifica el tipo, por defecto es i32
LengthSignedRangeUnsignedMax
8-biti8-128 hasta 127u8255
16-biti16-32_768 hasta 32_767u1665_535
32-biti32-2_147_483_648 hasta 2_147_483_647u324_294_967_295
64-biti64-9_223_372_036_854_775_808 hasta 9_223_372_036_854_775_807u6418_446_744_073_709_551_615
128-biti128---u128---
archisizeDependiendo la arquitecturausizeDependiendo la arquitectura
fn main() {
  let x_iint: i32 = -10;
  let y_uint: u64 = 20;

  let i32_max: i32 = 2147483647; // i32: 2^31 - 1
  let i64_max: i64 = 9223372036854775807; // std::i64::MAX
  
  println!("i8 MIN {}", i8::MIN); // -127
  println!("i8 MAX {}", i8::MAX); // 127
  println!("u8 MAX {}", u8::MAX); // 255
 
  println!("i16 MIN {}", i16::MIN); // -32_768
  println!("i16 MAX {}", i16::MAX); // 32_767
  println!("u16 MAX {}", u16::MAX); // 65_535
 
  println!("i32 MIN {}", i32::MIN); // -2_147_483_648
  println!("i32 MAX {}", i32::MAX); // 2_147_483_647
  println!("u32 MAX {}", u32::MAX); // 4_294_967_295
 
  println!("i64 MIN {}", i64::MIN); // -9_223_372_036_854_775_808
  println!("i64 MAX {}", i64::MAX); // 9_223_372_036_854_775_807
  println!("u64 MAX {}", u64::MAX); // 18_446_744_073_709_551_615
 
  println!("i128 MIN {}", i128::MIN);
  // -170_141_183_460_469_231_731_687_303_715_884_105_728
  println!("i128 MAX {}", i128::MAX);
  // 170_141_183_460_469_231_731_687_303_715_884_105_727
  println!("u128 MAX {}", u128::MAX);
  // 340_282_366_920_938_463_463_374_607_431_768_211_455
}

Signed

  • Cada variante con signo puede almacenar números desde -( 2^(n-1) ) hasta 2^(n-1) - 1
  • Por ejemplo: i8 puede almacenar -(2^(8-1)) hasta 2^(8-1) - 1 lo que equivale a -128 a 127

Unsigned

  • Las variantes sin signo pueden almacenar valores desde 0 hasta (2^n) - 1
  • Por ejemplo: (2^8) - 1 lo que equivale desde 0 hasta 255

arch

  • isize y usize
  • Son enteros cuyo tamaño depende de la arquitectura de la máquina (32 o 64 bits)
  • En una CPU de 32 bitsusize/isize ocupan 4 bytes (32 bits)
  • En una CPU de 64 bitsusize/isize ocupan 8 bytes (64 bits)

usize se usa principalmente para índices y tamaños:

  • Índices de arrays y vectores.
  • Resultados de .len() (siempre devuelve usize).
  • Operaciones de punteros bajos.

isize se usa raramente, pero puede servir para:

  • Desplazamientos relativos en memoria.
  • Casos donde se necesite un índice que puede ser negativo.

Formas de escribir enteros

Number literalsExample
Decimal98_222 (_ para facilitar la lectura)
Hex0xff
Octal0o77
Binary0b1111_0000
Byte (u8 only)b'A'

Integer overflow

  • Desbordamiento de enteros

Cuando una variable (ej. u8 que puede almacenar valores de 0 a 255) intenta contener un valor fuera de rango (como 256), ocurre un desbordamiento de enteros , lo que puede dar lugar a uno de dos comportamientos.

Compilación en modo de depuración En modo de depuración (modo por defecto), Rust incluye comprobaciones que hacen que el programa entre en pánico (panic) si ocurre un desbordamiento.

Compilación en modo lanzamiento con --release En modo de lanzamiento (con --release), Rust no incluye estas comprobaciones. En su lugar, realiza un "ajuste de complemento a dos" (two's complement wrapping).

Significa que, los valores mayores al valor máximo que el tipo puede contener "se ajustan" al mínimo de los valores que el tipo puede contener.

En el caso de u8 donde el máximo es 255

  • 256 se convierta en 0
  • 257 se convierta en 1
  • etc.

El programa no entrará en pánico, pero la variable tendrá un valor que probablemente no sea el que se espera que tenga.

fn main() {
    let mut num: u8 = 255;
    println!("Valor original: {}", num); // 255
   
    // Desbordamiento
    num = num + 1;
    println!("Después de sumar 1 (256): {}", num); // 0

    num = num + 1;
    println!("Después de sumar 1 otra vez (257): {}", num); // 1
}

En otros contextos, sí puede marcar error al intentar asignar un numero mayor al permitido, por ejemplo con parse(). Cuando se usa parse() para convertir una cadena en un número, Rust utiliza el método FromStr::from_str() que tiene sus propias reglas de manejo de errores.

Float (Floating-point numbers)

  • Valores con decimales
  • Solo existen dos de datos primitivos para float, f32 y f64
  • El tipo predeterminado es f64 porque en las CPU modernas tienen aproximadamente la misma velocidad que f32 pero es capaz de lograr mayor precisión
  • Todos los tipos de float son signed
  • Se representan según el estándar IEEE-754
  • f32 es un float de precisión simple, precisión aproximada de 7 dígitos decimales
  • f64 es un float de precisión doble, precisión aproximada de 15-17 dígitos decimales
fn main() {

  let x: f32 = 5.5;
  let pi: = 3.14; // f64 default
 
  println!("Pi: {}", pi);
 
  println!("f32 MIN {}", f32::MIN);
  // -340_282_350_000_000_000_000_000_000_000_000_000_000
  println!("f32 MAX {}", f32::MAX);
  // 340_282_350_000_000_000_000_000_000_000_000_000_000
 
  println!("f64 MIN {}", f64::MIN);
  println!("f64 MAX {}", f64::MAX);
}

Operaciones aritméticas

  • Rust admite las operaciones matemáticas básicas que se esperan para todos los tipos de números: suma, resta, multiplicación, división y resto
  • La división de números enteros trunca hacia cero hasta el número entero más cercano
fn main() {
// addition
let sum = 5 + 10;

// subtraction
let difference = 95.5 - 4.3;

// multiplication
let product = 4 * 30;

// division
let quotient = 56.7 / 32.2;
let truncated = -5 / 3; // Results in -1

// remainder (residuo o modulo)
let remainder = 43 % 5;
}

Boolean

  • bool
  • Solo tiene dos posibles valores; true o false
  • Tienen un tamaño de un byte (8 bits)
fn main() {
  let is_monday: bool = true;
  println!("Is Monday?: {}", is_monday);
}

Char

  • char representa un Unicode Scalar Value, no un “carácter” en el sentido humano
  • Se escriben con 'comillas simples'. Un String o &str usa comillas dobles ("...").
  • Siempre ocupa 4 bytes (32 bits)
  • Rango válido: de U+0000 hasta U+D7FF y de U+E000 hasta U+10FFFF. (Se salta U+D800U+DFFF porque ahí viven los surrogates de UTF-16)
  • Diferencia con ASCII: incluye no solo letras latinas, sino también caracteres acentuados, kanji, emojis, símbolos matemáticos, espacios invisibles, etc.

Métodos útiles (del trait char):

  • is_alphabetic()
  • is_numeric()
  • is_whitespace()
  • to_lowercase(), to_uppercase()
fn main() {
let crab = '🦀'; // un emoji es un char
let accent = 'é'; // un char con acento
let decomposed = ['e', '\u{0301}']; // mismo carácter, pero en 2 chars

println!("Crab: {}", crab);
println!("Accent: {}", accent);
println!("Decomposed: {}{}", decomposed[0], decomposed[1]);

println!("¿'7' es numérico? {}", '7'.is_numeric());
println!("¿'七' es numérico? {}", '七'.is_numeric()); // Kanji para 7 → false
}