我正在尝试围绕std::<T>::from_str_radix构建一个通用包装器。根据文档,from_str_radix返回Result<T, ParseIntError>。但

fn foo<T: num_traits::Num>() -> Result<T, std::num::ParseIntError> {
    T::from_str_radix("4242", 10)
}

不会编译:



另一方面,这
fn main() {
    let x: Result<u8, std::num::ParseIntError> = foo();
    println!("{:?}", x);
}

fn foo<T: num_traits::Num>() -> Result<T, <T as num_traits::Num>::FromStrRadixErr> {
    T::from_str_radix("4242", 10)
}

编译良好并打印预期结果



在我看来,这两种情况都是一样的,但显然我是错的。有人可以向我解释差异并为我提供解决方案吗?

最佳答案



这是不可能的。某些错误(例如 io::Error )使您可以创建带有导致错误的实例,因此您可以创建包含另一个错误的错误。如果ParseIntError具有此类功能,则可以创建由ParseIntError错误引起的FromStrRadixErr,但ParseIntError则不然。



这段代码:

fn foo<T: num_traits::Num>() -> Result<T, std::num::ParseIntError> {
    T::from_str_radix("4242", 10)
}

无法编译,因为返回的T::from_str_radix类型是Result<T, FromStrRadixErr>而不是Result<T, ParseIntError>。将返回类型(如您所做的那样)更改为Result<T, FromStrRadixErr>可以解决此问题。

这段代码:
fn main() {
    let x: Result<u8, std::num::ParseIntError> = foo();
    println!("{:?}", x);
}

fn foo<T: num_traits::Num>() -> Result<T, <T as num_traits::Num>::FromStrRadixErr> {
    T::from_str_radix("4242", 10)
}

可以正常编译,因为Numu8 trait实现defines FromStrRadixErr = ParseIntError

如果将u8更改为f32:
let x: Result<f32, std::num::ParseIntError> = foo();

它无法编译。 Num defines f32FromStrRadixErr = ParseFloatErrorParseFloatError实现不是ParseIntError的原因。



您说过您正在尝试围绕std::<T>::from_str_radix构建一个通用包装器,但是您的示例在T::from_str_radix中使用了T: Num,因此您正在尝试围绕Num::from_str_radix编写包装器。

一种选择是直接使用NumFromStrRadixErr而不是创建包装器,最后Num是包装器。

也许您想将包装器限制为原始整数,并使用ParseIntError。在这种情况下,您可以添加限制FromStrRadixErr = ParseIntError:
fn foo<T>() -> Result<T, std::num::ParseIntError>
    where T: num_traits::Num<FromStrRadixErr = std::num::ParseIntError>,
{
    T::from_str_radix("4242", 10)
}

10-07 15:44