Here is a kind of lengthy example because I was not able to reduce it further. 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;
}
Can someone explain why I am getting overflow evaluating the requirement <_ as Pixel>::ScalarType in that code snippet?
I am confused because:
- If the
Addimplementation in line 44 is removed the code compiles fine. But that implementation should not be used at all =>main()only usesGrayand notImage - The recursion seems to be in nested
Image<Image<...>>structs but that should not happen at all?! - If line 46 is changed to
ScalarVal<<PixelP as Pixel>::ScalarType>: Addit compiles fine - But why?
Some context
This is part of an image processing library I want to build. The idea is to have different pixel formats (Gray, RGB, Bayer, ...) which can be used for images. Therefore you have Image<Gray> for a grayscale image. Different Pixel implementations can implement different operators, so you can do calculations (e.g. gray_a = gray_b + gray_c) with them. It is also possible to use scalar values in those implementations to do e.g. gray_a = gray_b + ScalarVal(42). Because I want to make it possible to have ScalarVal as right- and left-hand-argument there need to be two implementations (impl Add<Gray<...>> for ScalarVal<...> and impl Add<ScalarVal<...>> for Gray<...>).
Ok and now the Image type should implement all operators which are supported by the used Pixel type. If it is possible to do gray_pixel + Scalar(42) it should also be possible to do gray_image + Scalar(42).
Hope this kind of makes sense.