如何在 u32 和 usize 之间进行惯用转换?

Posted

技术标签:

【中文标题】如何在 u32 和 usize 之间进行惯用转换?【英文标题】:How to idiomatically convert between u32 and usize? 【发布时间】:2017-09-28 00:31:14 【问题描述】:

此代码有效并打印“b”:

fn main() 
    let s = "abc";
    let ch = s.chars().nth(1).unwrap();
    println!("", ch);

另一方面,此代码会导致类型不匹配错误。

fn main() 
    let s = "abc";
    let n: u32 = 1;
    let ch = s.chars().nth(n).unwrap();
    println!("", ch);

error[E0308]: mismatched types
 --> src/main.rs:5:28
  |
5 |     let ch = s.chars().nth(n).unwrap();
  |                            ^ expected usize, found u32

由于某些外部原因,我必须将 u32 类型用于变量 n。如何将u32 转换为usize 并在nth() 中使用?

【问题讨论】:

usize 转换为u32 或反之亦然有哪些常见用例? 【参考方案1】:

as 运算符适用于所有数字类型:

let ch = s.chars().nth(n as usize).unwrap();

Rust 强制你转换整数以确保你知道有符号或溢出。

整数常量可以有类型后缀:

let n = 1u32;

但是,请注意负常数,例如 -1i32 在内部是 - 1i32

在没有明确类型说明的情况下声明的整数变量显示为integer,并且将从方法调用之一正确推断。

【讨论】:

为了保护盲目复制和粘贴此答案的人:我认为@Shepmaster (***.com/a/55769098/1290383) 的答案应该是“已接受”的答案。有人可能会在这里偶然发现这个答案,认为使用 as 可以节省数字转换,但事实并非如此。【参考方案2】:

您可以做的最谨慎的事情是使用TryFrom 并在值无法放入usize 时恐慌:

use std::convert::TryFrom;

fn main() 
    let s = "abc";
    let n: u32 = 1;
    let n_us = usize::try_from(n).unwrap();
    let ch = s.chars().nth(n_us).unwrap();
    println!("", ch);

盲目地使用as,当你的代码在usize小于32位的平台上运行时,你的代码会以一种神秘的方式失败。例如,一些微控制器使用 16 位整数作为原生大小:

fn main() 
    let n: u32 = 0x1_FF_FF;
    // Pretend that `usize` is 16-bit
    let n_us: u16 = n as u16;
    
    println!(", ", n, n_us); // 131071, 65535

对于u32 以外的更广泛的数字转换类型 usize,请参阅How do I convert between numeric types safely and idiomatically?。

【讨论】:

我认为在使用TryFrom 之前,对于某些情况,开发人员还可以使用From 特征(例如u32::from(n)。如果演员表不是,这应该会在编译时发出错误在 Rust 编译器中实现。但是:据我所见,isizeusizeFrom 特征仅针对 u8 和 u16 实现,但根据平台不进行检查(参见 doc.rust-lang.org/src/core/convert/num.rs.html#44 和doc.rust-lang.org/src/core/convert/num.rs.html#135)。这可能是 8 位微控制器的问题(不确定 Rust 是否支持它们) @obraunsdorf 链接到涵盖该问题的现有答案,谢谢。【参考方案3】:

当我们尝试编译您的代码时,我们现在有一个完全不同的答案,将数字 1 替换为 i32 类型的变量:

error[E0308]: mismatched types
 --> src/main.rs:5:28
  |
5 |     let ch = s.chars().nth(n).unwrap();
  |                            ^ expected usize, found i32
help: you can convert an `i32` to `usize` and panic if the converted value wouldn't fit
  |
5 |     let ch = s.chars().nth(n.try_into().unwrap()).unwrap();
  |    

这意味着现在编译器建议您使用 n.try_into().unwrap(),它利用了 TryInto 特性,而 TryInto 又依赖于 TryFrom 并返回 Result<T, T::Error>。这就是为什么我们需要用.unwrap()提取结果

TryInto documentation

【讨论】:

这实际上已经被existing answer 覆盖,因为TryFrom / TryInto 是彼此的镜像。

以上是关于如何在 u32 和 usize 之间进行惯用转换?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 wstring_convert 在 utf16 和 utf32 之间进行转换?

用于在随机加权选择之间进行选择的惯用 Clojure

如何在 C++11 中将 std::string 转换为 std::u32string?

如何使用 ARM NEON 内在函数将 u8 掩码转换为 u32 掩码?

如何在 Rust 中惯用地将 bool 转换为 Option 或 Result?

如何在返回新接收的值和缓存值之间的所有组合的迭代器时修复生命周期问题?