使用约束实现结构体的From/Into方法

3

由于.max()对于f64无效,我正在编写一个ForceOrd结构体来确保参数不是NaN。预期使用方法如下:

let m = xs.iter().map(|&x| ForceOrd(x)).max().unwrap().into();

然而,我无法编译出符合Into特性的实现,出现以下错误:
conflicting implementations of trait `std::convert::Into<_>` for type `ForceOrd<_>`

这里是代码(在线示例):

#[derive(PartialEq, PartialOrd)]
pub struct ForceOrd<X: PartialEq + PartialOrd>(pub X);
impl<X: PartialEq + PartialOrd> Eq for ForceOrd<X> { }
impl<X: PartialEq + PartialOrd> Ord for ForceOrd<X> {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.0.partial_cmp(&other.0).unwrap()
    }
}
/// doesn't work
impl<X: PartialEq + PartialOrd> Into<X> for ForceOrd<X> {
    fn into(x: Self) -> X { x.0 }
}
/// doesn't work either
impl<X: PartialEq + PartialOrd> From<ForceOrd<X>> for X {
    fn from(x: ForceOrd<X>) -> Self { x.0 }
}

放宽对此的通用约束并直接在f64上实现,这样行不行?这对你来说不是一个选项吗?我认为不可能在跨crate的开放泛型中添加trait限制(如果你只实现From,那么这应该是你得到的错误)。 - Simon Whitehead
1个回答

5

单个类型无法同时实现FromInto,即如果您实现了impl From<ForceOrd<X>> for X, 那么您就不能再实现impl Into<X> for ForceOrd<X>。而且您只需要其中之一。正如IntoFrom的文档所述:

From<T> for U implies Into<U> for T

您应该选择只使用 From 实现。您可以查看以下问题,了解在一般情况下应该选择哪个:When should I implement std::convert::From vs std::convert::Into?

编辑:由于在这种情况下实现From不是(简单地去掉impl Into),因此您可以看到如何为f64实现:

#[derive(PartialEq, PartialOrd, Debug)]
pub struct ForceOrd<X: PartialEq + PartialOrd>(pub X);

impl<X: PartialEq + PartialOrd> Eq for ForceOrd<X> { }

impl<X: PartialEq + PartialOrd> Ord for ForceOrd<X> {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.0.partial_cmp(&other.0).unwrap()
    }
}

impl<X: PartialEq + PartialOrd> From<X> for ForceOrd<X> {
    fn from(x: X) -> ForceOrd<X> {
        ForceOrd(x)
    }
}

fn main() {
    let xs = vec![1.1f64, 3.5, 2.2];

    let max = xs.iter().map(|&f| <ForceOrd<f64>>::from(f)).max().unwrap();

    println!("{:?}", max); // prints "ForceOrd(3.5)"
}

不幸的是,我想这就是你能达到的最远程度了;你将无法实现以下功能:

impl<X: PartialEq + PartialOrd> From<ForceOrd<X>> for X

能够完成最终版

<f64>::from(xs.iter().map(|&f| <ForceOrd<f64>>::from(f)).max().unwrap())

因为f64不在本地创建。您可以通过阅读Niko Matsakis的这篇非常详细的博客文章来了解更多关于此限制的信息,并查看StackOverflow中的此问题。

我认为 OP 分别尝试了每个。问题在于尝试实现一个 trait,其中泛型类型具有在当前 crate 中未声明的 trait 约束。我认为这是不可能的。在标准库中是可能的,因为那里是原始 trait 的所在地。 - Simon Whitehead
1
我扩展了我的答案,并提供了一个解决方案,适用于只有一个impl的情况。 - ljedrz

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