“溢出评估需求”但这种递归根本不应该发生

时间:2016-09-08 17:40:47

标签: generics rust

这是一个冗长的例子,因为我无法进一步减少它。 Rust Playground

use std::marker::PhantomData;
use std::ops::{Add, Sub, Mul, Div};

pub trait Scalar
    : Sized + Copy + Add<Self, Output = Self> + Sub<Self, Output = Self> + Mul<Self, Output = Self> + Div<Self, Output = Self>
    {}
impl Scalar for u32 {}

pub struct ScalarVal<T>(T) where T: Scalar;

pub trait Pixel: Sized {
    type ScalarType: Scalar;
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Gray<BaseTypeP>
    where BaseTypeP: Scalar
{
    intensity: BaseTypeP,
}

impl<BaseTypeP> Pixel for Gray<BaseTypeP>
    where BaseTypeP: Scalar
{
    type ScalarType = BaseTypeP;
}

impl<BaseTypeP> Add<Gray<BaseTypeP>> for ScalarVal<BaseTypeP>
    where BaseTypeP: Scalar
{
    type Output = Gray<BaseTypeP>;
    fn add(self, rhs: Gray<BaseTypeP>) -> Gray<BaseTypeP> {
        unimplemented!()
    }
}

pub struct Image<PixelP>
    where PixelP: Pixel
{
    _marker: PhantomData<PixelP>,
}

impl<PixelP> Add<Image<PixelP>> for ScalarVal<<PixelP as Pixel>::ScalarType>
    where PixelP: Pixel,
          ScalarVal<<PixelP as Pixel>::ScalarType>: Add<PixelP, Output = PixelP>
{
    type Output = Image<PixelP>;
    fn add(self, rhs: Image<PixelP>) -> Image<PixelP> {
        unimplemented!()
    }
}

fn main() {
    let a = Gray::<u32> { intensity: 41 };
    let b = ScalarVal(1) + a;
}

有人可以解释我在该代码段中获取overflow evaluating the requirement <_ as Pixel>::ScalarType的原因吗?

我很困惑因为:

  • 如果第44行中的Add实现被删除,则代码编译正常。但该实现不应该全部使用=&gt; main()仅使用Gray而非Image
  • 递归似乎是嵌套的Image<Image<...>>结构,但这根本不会发生?!
  • 如果第46行更改为ScalarVal<<PixelP as Pixel>::ScalarType>: Add,则编译正常 - 但为什么?

某些背景

这是我想要构建的图像处理库的一部分。我们的想法是拥有可用于图像的不同像素格式(灰色,RGB,拜耳......)。因此,对于灰度图像,您有Image<Gray>。不同的Pixel实现可以实现不同的运算符,因此您可以使用它们进行计算(例如gray_a = gray_b + gray_c)。在这些实现中也可以使用标量值来实现例如标量值。 gray_a = gray_b + ScalarVal(42)。因为我想让ScalarVal作为right- 左手参数成为可能,所以需要有两个实现(impl Add<Gray<...>> for ScalarVal<...>impl Add<ScalarVal<...>> for Gray<...>)。

好的,现在Image类型应该实现所使用的Pixel类型支持的所有运算符。如果可以gray_pixel + Scalar(42),则也可以执行gray_image + Scalar(42)

希望这是有道理的。

1 个答案:

答案 0 :(得分:6)

ScalarVal(1) + a基本上解析为<ScalarVal(1) as Add>.add(a),它会在Add上查找ScalarVal实施。

无论出于何种原因,首先检查这个:

impl<PixelP> Add<Image<PixelP>> for ScalarVal<<PixelP as Pixel>::ScalarType>
    where PixelP: Pixel,
          ScalarVal<<PixelP as Pixel>::ScalarType>: Add<PixelP, Output = PixelP>
此时

PixelP未实例化,因此无法检查PixelP: Pixel。因此我们进入

ScalarVal<<PixelP as Pixel>::ScalarType>: Add<PixelP, Output = PixelP>

让我们简化一下。由于PixelP目前还不知道,<PixelP as Pixel>::ScalarType未知。编译器已知的实际信息看起来更像

impl<T> Add<Image<T>> for ScalarVal<_>
    where ScalarVal<_>: Add<T, Output = T>

因此ScalarVal<_>会查找Add<T, Output = T>。这意味着我们应该寻找合适的T。显然,这意味着要查看ScalarVal的{​​{1}} Add。看着同一个,我们得到

impl

表示如果此匹配,impl<T2> Add<Image<T2>> for ScalarVal<_> where ScalarVal<_>: Add<T2, Output = T2>

显然然后是T == Image<T2>T2 == Image<T3>等。这会导致溢出和普遍的悲伤。 Rust从未找到 disproof ,因此无法保证它会走错路。