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,trueya - 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
| Length | Signed | Range | Unsigned | Max |
|---|---|---|---|---|
| 8-bit | i8 | -128 hasta 127 | u8 | 255 |
| 16-bit | i16 | -32_768 hasta 32_767 | u16 | 65_535 |
| 32-bit | i32 | -2_147_483_648 hasta 2_147_483_647 | u32 | 4_294_967_295 |
| 64-bit | i64 | -9_223_372_036_854_775_808 hasta 9_223_372_036_854_775_807 | u64 | 18_446_744_073_709_551_615 |
| 128-bit | i128 | --- | u128 | --- |
| arch | isize | Dependiendo la arquitectura | usize | Dependiendo 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) )hasta2^(n-1) - 1 - Por ejemplo:
i8puede almacenar-(2^(8-1))hasta2^(8-1) - 1lo que equivale a -128 a 127
Unsigned
- Las variantes sin signo pueden almacenar valores desde
0hasta(2^n) - 1 - Por ejemplo:
(2^8) - 1lo que equivale desde0hasta255
arch
isizeyusize- Son enteros cuyo tamaño depende de la arquitectura de la máquina (32 o 64 bits)
- En una CPU de 32 bits →
usize/isizeocupan 4 bytes (32 bits) - En una CPU de 64 bits →
usize/isizeocupan 8 bytes (64 bits)
usize se usa principalmente para índices y tamaños:
- Índices de arrays y vectores.
- Resultados de
.len()(siempre devuelveusize). - 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 literals | Example |
|---|---|
| Decimal | 98_222 (_ para facilitar la lectura) |
| Hex | 0xff |
| Octal | 0o77 |
| Binary | 0b1111_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
f64porque en las CPU modernas tienen aproximadamente la misma velocidad quef32pero es capaz de lograr mayor precisión - Todos los tipos de float son signed
- Se representan según el estándar IEEE-754
f32es un float de precisión simple, precisión aproximada de 7 dígitos decimalesf64es 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;
trueofalse - Tienen un tamaño de un byte (8 bits)
fn main() {
let is_monday: bool = true;
println!("Is Monday?: {}", is_monday);
}
Char
charrepresenta un Unicode Scalar Value, no un “carácter” en el sentido humano- Se escriben con
'comillas simples'. UnStringo&strusa comillas dobles ("..."). - Siempre ocupa 4 bytes (32 bits)
- Rango válido: de
U+0000hastaU+D7FFy deU+E000hastaU+10FFFF. (Se saltaU+D800–U+DFFFporque 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
}