如何使用TryFrom将usize转换为u32?

13

我想在Rust中将一个usize类型的变量转换为u32类型的变量。我知道usize变量可能包含一个大于2^32的值,如果是这种情况则转换应该失败。我正在尝试使用TryFrom特质来执行转换。

这是一个简单的示例(夜间版Rust,Playground):

#![feature(try_from)]
use std::convert::TryFrom;

fn main() {
    let a: usize = 0x100;
    let res = u32::try_from(a);
    println!("res = {:?}", res);
}

代码无法编译,出现以下编译错误:

error[E0277]: the trait bound `u32: std::convert::From<usize>` is not satisfied
 --> src/main.rs:6:15
  |
6 |     let res = u32::try_from(a);
  |               ^^^^^^^^^^^^^ the trait `std::convert::From<usize>` is not implemented for `u32`
  |
  = help: the following implementations were found:
            <u32 as std::convert::From<std::net::Ipv4Addr>>
            <u32 as std::convert::From<u8>>
            <u32 as std::convert::From<char>>
            <u32 as std::convert::From<u16>>
  = note: required because of the requirements on the impl of `std::convert::TryFrom<usize>` for `u32`

从编译错误中我推断出,对于u32来说,拥有TryFrom<usize>取决于是否拥有From<usize>,这似乎有点奇怪。

是否有其他方法可以利用TryFromusize转换为u32?如果没有,是否有其他惯用的方法执行此转换?

我知道可以使用 as 关键字,但它不会通知我转换是否失败。此外,我认为可以编写自己的函数来进行转换,但如果 Rust 没有某种惯用的方式来执行此转换,我会感到惊讶。毕竟,usizeu32是两种基本类型。

1个回答

16
自从创建了这个答案以来,决定实施TryFrom<usize>始终允许失败的可能性,无论当前平台如何。原始代码现在可以在Rust 1.34中成功编译。

原始答案

TryFrom<usize>对于u32的使用取决于是否有From<usize>对于u32的使用,这对我来说似乎有些奇怪

这是因为针对任何实现From的东西,都有一个TryFrom的模板实现:

impl<T, U> TryFrom<U> for T
where
    T: From<U>,
{
    type Error = !;
}

正如你所提到的,由于Rust支持本机整数长度为16、32或64位的平台,因此对From/Into进行这样的实现在某些平台上将不是无损的。
出现这个错误是因为这些类型没有直接的TryFrom/TryInto实现。这是因为这些特性的用户希望在适当的平台上实现是无错误的(type Error = !)。
有一个单独的跟踪问题49415专门用于解决这个问题。

我认为我可以编写自己的函数来进行转换。

是的,这就是你应该做的。像这段未经测试的代码一样:
use std::u32;

struct SomeError;

// usize is a u16 or u32, which always fits in a u32
#[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
fn my_thing(a: usize) -> Result<u32, SomeError> {
    Ok(a as u32)
}

// usize is a u64, which might be too big
#[cfg(target_pointer_width = "64")]
fn my_thing(a: usize) -> Result<u32, SomeError> {
    if a > u32::MAX as usize {
        Err(SomeError)
    } else {
        Ok(a as u32)
    }
}

我会很惊讶如果Rust没有一些惯用的方法来进行这种转换。毕竟,usizeu32是两种基本类型。问题在于usize并不是真正的“基本”类型,因为它的大小取决于目标平台。要正确、高效且符合人体工程学的解决这个问题并不容易。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接