如何实现 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() = u64
或 as_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)
}
}
请注意,只要您只使用无符号类型,unwrap
s 就永远不会失败,因为对类型大小的检查可确保转换始终有效。
我有一个函数 returns compound duration 基于 usize
输入:
pub fn format_dhms(seconds: usize) -> String
如果输入是6000000
:
println!("{}", format_dhms(6000000));
它returns:
69d10h40m
这在输入为数字时有效,但是当我使用另一个具有固定类型的函数的输出时,我需要使用as usize
。例如,如果我使用方法 as_secs() = u64
或 as_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)
}
}
请注意,只要您只使用无符号类型,unwrap
s 就永远不会失败,因为对类型大小的检查可确保转换始终有效。