如何实现 Into 特征以将所有输入转换为 usize 而不是使用“as usize”?

How to implement Into trait to convert all input to usize not using `as usize`?

我有一个函数 returns compound duration 基于 usize 输入:

pub fn format_dhms(seconds: usize) -> String 

如果输入是6000000:

println!("{}", format_dhms(6000000));

它returns:

69d10h40m

这在输入为数字时有效,但是当我使用另一个具有固定类型的函数的输出时,我需要使用as usize。例如,如果我使用方法 as_secs() = u64as_nanos() = u128.

使用 Duration 的输出

当有人通过 u128::MAX 时,我想像 usize 那样处理它,将输入截断为最大可接受值。

这就是我正在尝试的:(https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=4a8bfa152febee9abb52d8244a5092c5)

#![allow(unused)]
use std::time::Instant;

fn format<T: Into<usize>>(number: T) {
    if number == 0 {
        //println!("{}", number)
    } else {
        //println!("{}> 0", number)
    }
}

fn main() {
    let now = Instant::now();
    format(now.elapsed().as_nanos()); // u128
    format(now.elapsed().as_secs()); // u64
}

但我得到的一些错误是:

error[E0277]: the trait bound `usize: std::convert::From<i32>` is not satisfied
    the trait `std::convert::From<i32>` is not implemented for `usize`
...
error[E0369]: binary operation `==` cannot be applied to type `T`

如果我删除 <T: Into<size>> 它可以工作,但我需要使用 as usize

 format(now.elapsed().as_nanos() as usize);

有没有一种方法可以转换输入以防止使用 as usize 或者当输入只是一个没有定义类型的数字时如何实现相同的行为?

使用 TryFrom 特性,您可以 "try" 转换为不同的类型。如果输入的数字对于 usize 来说太大,您将得到一个错误。

fn foo<T: TryInto<usize>>(x: T) -> usize {
    x.try_into().unwrap() // Will panic if
                          // x cannot fit
                          // into a usize.
}

此外,这与 as 转换没有相同的语义效果。因为那些会被截断,而这将行不通。


在这种情况下,真正的最佳做法是只对数字使用常规特征边界,而不是使用 usize,因为某些值不适合那里:

fn format<
    T: Sub<Output = T> + 
       Mul<Output = T> + 
       Div<Output = T> + 
       Display + 
       PartialEq +
       From<bool>        //etc. for all the operations you need. 
    >(number: T) {
    if number == T::from(false) { // `false` turns into 0 for numbers. 
        //println!("{}", number)
    } else {
        //println!("{}> 0", number)
    }
}

然而,std 数字特征相当准系统,因此我建议您查看 num_traits

您可以使用 std::mem::size_of 检查输入类型是否适合 usize 并在不适合时使用位操作截断:

use std::convert::{ TryFrom, TryInto };
use std::fmt::Debug;
use std::ops::BitAnd;
use std::time::Instant;

fn format<T: TryInto<usize> + TryFrom<usize> + BitAnd<Output=T>> (number: T)
    where <T as TryFrom<usize>>::Error: Debug,
          <T as TryInto<usize>>::Error: Debug
{
    let number: usize = if std::mem::size_of::<T>() <= std::mem::size_of::<usize>() {
        number.try_into().unwrap()
    } else {
        (number & usize::MAX.try_into().unwrap()).try_into().unwrap()
    };
    if number == 0 {
        //println!("{}", number)
    } else {
        //println!("{}> 0", number)
    }
}

Playground

请注意,只要您只使用无符号类型,unwraps 就永远不会失败,因为对类型大小的检查可确保转换始终有效。