casting - programacion - ¿Cómo convierto entre tipos numéricos de forma segura e idiomática?
tipos de datos en pascal (1)
Nota del editor: esta pregunta es de una versión de Rust anterior a 1.0 y hace referencia a algunos elementos que no están presentes en Rust 1.0. Las respuestas aún contienen información valiosa.
¿Cuál es la forma idiomática de convertir (por ejemplo) un
usize
a un
u32
?
Por ejemplo, la conversión usando
4294967295us as u32
funciona y los
documentos de referencia de Rust 0.12 sobre el tipo de conversión
dicen
Un valor numérico se puede convertir a cualquier tipo numérico. Un valor de puntero sin formato se puede convertir ao desde cualquier tipo integral o tipo de puntero sin formato. Cualquier otro elenco no es compatible y no se compilará.
pero
4294967296us as u32
se desbordará silenciosamente y dará un resultado de 0.
Encontré
ToPrimitive
y
FromPrimitive
que proporcionan buenas funciones como
to_u32() -> Option<u32>
, pero están marcadas como inestables:
#[unstable(feature = "core", reason = "trait is likely to be removed")]
¿Cuál es la forma idiomática (y segura) de convertir entre tipos numéricos (y punteros)?
El tamaño dependiente de la
isize
de
isize
/
usize
es una de las razones por las que hago esta pregunta: el escenario original era que quería convertir de
u32
a
usize
para poder representar un árbol en un
Vec<u32>
(por ejemplo,
let t = Vec![0u32, 0u32, 1u32]
, entonces obtener el abuelo del nodo 2 sería
t[t[2us] as usize]
), y me preguntaba cómo fallaría si
usize
fuera menos de 32 bits.
En
ToPrimitive
/
FromPrimitive
RFC 369, Num Reform, establece :
Idealmente, [...]
ToPrimitive
[...] se eliminaría en favor de una forma más basada en principios de trabajar con enumeraciones tipo C
Mientras tanto, estos rasgos viven en la caja numérica :
Tratar sin los rasgos
De un tipo que encaja completamente dentro de otro
No hay problema aquí.
Utilice
From
para ser explícito de que no se producen pérdidas:
fn example(v: i8) -> i32 {
i32::from(v) // or v.into()
}
Puede elegir usar
as
, pero se recomienda evitarlo cuando no lo necesite (ver más abajo):
fn example(v: i8) -> i32 {
v as i32
}
De un tipo que no cabe completamente en otro
No existe un método único que tenga sentido general: está preguntando cómo encajar dos cosas en un espacio destinado a una.
Un buen intento inicial es usar una
Option
:
Some
cuando el valor se ajusta y
None
contrario.
Luego puede fallar su programa o sustituir un valor predeterminado, según sus necesidades.
Desde Rust 1.34, puede usar
TryFrom
:
use std::convert::TryFrom;
fn example(v: i32) -> Option<i8> {
i8::try_from(v).ok()
}
Antes de eso, tendría que escribir un código similar usted mismo:
fn example(v: i32) -> Option<i8> {
if v > std::i8::MAX as i32 {
None
} else {
Some(v as i8)
}
}
Que
as
pero
4294967296us as u32
se desbordará silenciosamente y dará un resultado de 0
Al convertir a un tipo más pequeño,
as
solo toma los bits más bajos del número, sin tener en cuenta los bits superiores, incluido el signo:
fn main() {
let a: u16 = 0x1234;
let b: u8 = a as u8;
println!("0x{:04x}, 0x{:02x}", a, b); // 0x1234, 0x34
let a: i16 = -257;
let b: u8 = a as u8;
println!("0x{:02x}, 0x{:02x}", a, b); // 0xfeff, 0xff
}