如何安全且惯用地在数字类型之间进行转换? [英] How do I convert between numeric types safely and idiomatically?

查看:18
本文介绍了如何安全且惯用地在数字类型之间进行转换?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

<块引用>

编者注:此问题来自 Rust 1.0 之前的版本,并引用了 Rust 1.0 中不存在的一些项目.答案仍然包含有价值的信息.

从(比如)usize 转换为 u32 的惯用方法是什么?

例如,使用 4294967295us as u32 进行转换,并且 关于类型转换的 Rust 0.12 参考文档

<块引用>

数值可以转换为任何数值类型.可以将原始指针值转换为任何整数类型或原始指针类型或从任何整数类型或原始指针类型转换.任何其他类型的转换均不受支持且无法编译.

但是 4294967296us as u32 会悄悄溢出并给出 0 的结果.

我找到了 ToPrimitiveFromPrimitive 提供了很好的功能,比如 to_u32() ->选项,但它们被标记为不稳定:

<块引用>

#[unstable(feature = "core", reason = "trait 可能会被移除")]

在数字(和指针)类型之间转换的惯用(和安全)方式是什么?

isize/usize 的平台相关大小是我问这个问题的一个原因 - 最初的场景是我想从 u32 转换usize 以便我可以在 Vec 中表示一棵树(例如 let t = Vec![0u32, 0u32, 1u32],然后获取节点 2 的祖父节点将是 t[t[2us] as usize]),我想知道如果 usize 它将如何失败小于 32 位.

解决方案

转换值

来自完全适合另一种类型的类型

这里没有问题.使用 From trait 是明确表示没有损失发生:

fn 示例(v: i8) ->i32 {i32::from(v)//或 v.into()}

您可以选择使用 as,但建议您在不需要时避免使用它(见下文):

fn 示例(v: i8) ->i32 {v 作为 i32}

来自一种不完全适合另一种的类型

没有一种方法具有普遍意义 - 您正在询问如何在一个空间中放置两件东西.一个很好的初始尝试是使用 OptionSome 当值适合时使用 None 否则.然后,您可以根据需要使程序失败或替换默认值.

从 Rust 1.34 开始,您可以使用 TryFrom:

使用 std::convert::TryFrom;fn 示例(v: i32) ->选项{i8::try_from(v).ok()}

在此之前,您必须自己编写类似的代码:

fn 示例(v: i32) ->选项{如果 v >std::i8::MAX 作为 i32 {没有任何} 别的 {一些(v as i8)}}

as 做什么

<块引用>

但是 4294967296us as u32 会悄悄溢出并给出 0 的结果

当转换为更小的类型时,as 只取数字的低位,不考虑高位,包括符号:

fn main() {让一个:u16 = 0x1234;让 b: u8 = a as u8;println!("0x{:04x}, 0x{:02x}", a, b);//0x1234, 0x34让 a: i16 = -257;让 b: u8 = a as u8;println!("0x{:02x}, 0x{:02x}", a, b);//0xfeff, 0xff}

另见:

关于ToPrimitive/FromPrimitive

RFC, Num Reform, 声明:

<块引用>

理想情况下 [...] ToPrimitive [...] 都将被删除,以支持更原则的方式来处理类似 C 的枚举

与此同时,这些特征仍然存在于num crate中:

Editor's note: This question is from a version of Rust prior to 1.0 and references some items that are not present in Rust 1.0. The answers still contain valuable information.

What's the idiomatic way to convert from (say) a usize to a u32?

For example, casting using 4294967295us as u32 works and the Rust 0.12 reference docs on type casting say

A numeric value can be cast to any numeric type. A raw pointer value can be cast to or from any integral type or raw pointer type. Any other cast is unsupported and will fail to compile.

but 4294967296us as u32 will silently overflow and give a result of 0.

I found ToPrimitive and FromPrimitive which provide nice functions like to_u32() -> Option<u32>, but they're marked as unstable:

#[unstable(feature = "core", reason = "trait is likely to be removed")]

What's the idiomatic (and safe) way to convert between numeric (and pointer) types?

The platform-dependent size of isize / usize is one reason why I'm asking this question - the original scenario was I wanted to convert from u32 to usize so I could represent a tree in a Vec<u32> (e.g. let t = Vec![0u32, 0u32, 1u32], then to get the grand-parent of node 2 would be t[t[2us] as usize]), and I wondered how it would fail if usize was less than 32 bits.

解决方案

Converting values

From a type that fits completely within another

There's no problem here. Use the From trait to be explicit that there's no loss occurring:

fn example(v: i8) -> i32 {
    i32::from(v) // or v.into()
}

You could choose to use as, but it's recommended to avoid it when you don't need it (see below):

fn example(v: i8) -> i32 {
    v as i32
}

From a type that doesn't fit completely in another

There isn't a single method that makes general sense - you are asking how to fit two things in a space meant for one. One good initial attempt is to use an OptionSome when the value fits and None otherwise. You can then fail your program or substitute a default value, depending on your needs.

Since Rust 1.34, you can use TryFrom:

use std::convert::TryFrom;

fn example(v: i32) -> Option<i8> {
    i8::try_from(v).ok()
}

Before that, you'd have to write similar code yourself:

fn example(v: i32) -> Option<i8> {
    if v > std::i8::MAX as i32 {
        None
    } else {
        Some(v as i8)
    }
}

What as does

but 4294967296us as u32 will silently overflow and give a result of 0

When converting to a smaller type, as just takes the lower bits of the number, disregarding the upper bits, including the sign:

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
}

See also:

About ToPrimitive / FromPrimitive

RFC 369, Num Reform, states:

Ideally [...] ToPrimitive [...] would all be removed in favor of a more principled way of working with C-like enums

In the meantime, these traits live on in the num crate:

这篇关于如何安全且惯用地在数字类型之间进行转换?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆