Use twice same generic type in impl

Hello all,

I need your help. I don't know if what I want to do is possible.

I have a trait like this:

pub trait ItemProcessor<R, W> {
    fn process<'a>(&'a self, item: &'a R) -> W;
}

For some cases, processing is just a copy: W is the same struct of R.

So i would like to implement a default processor, which just copies:

#[derive(Default)]
pub struct DefaultProcessor {
}
impl<R: Clone> ItemProcessor<R, R> for DefaultProcessor {
    fn process<'a>(&'a self, item: &'a R) -> R
    where
        DefaultProcessor: ItemProcessor<R, R>,
    {
        item.clone()
    }
}

And when i create an instance:

let processor: &dyn ItemProcessor<R, W> = if let Some(processor) = self.processor {
    processor
} else {
    let test: &dyn ItemProcessor<R, R> = &DefaultProcessor{};
    test
};

The compiler said: expected reference &dyn ItemProcessor<R, W>

Do you have a soultion or another way to do this.

Thanks a lot for your help,

Simon

The problem here is that you're using different Ws in the two branches of your if, so the compiler can't find a single type &dyn ItemProcessor<R, ??> for the variable processor. To make this work, you'll need to figure out how to make the second type parameter either go away or be the same in both branches. There's several approaches to do this, but they all have different tradeoffs— It's hard to give more concrete advice without more details about how ItemProcessor is used.

Thanks for your help !

Actually I want to cast generic R to W like this:

impl<R, W> ItemProcessor<R, W> for DefaultProcessor {
    fn process<'a>(&'a self, item: &'a R) -> W
    {
        item as W
    }
}

Because for DefaultProcessor R is the same struct as W.

But I don't know how to do it. I tried "where R: W" but is juste for Trait.

Consider this problem: what should your code do if R is not equal to W and self.processor is None? That's essentially why you have a type error — your code is assuming that if self.processor is None, then R must be equal to W, but that is trying to take run-time information back to the type checking, which is not possible.

To satisfy the compiler, you will have to have a plan for what happens if R is not equal to W and self.processor is None. There might be further problems, but at a minimum you need an answer to that question.

1 Like

Hi kpreid, thanks for your help.

Actually, ItemProcessor is useful only when we have to convert an item from R to W. In this case the user create an object which implements ItemProcessor trait. In this case, is Some. When we don't have to transform, the ItemProcessor is None and I want to force to use the default one "DefaultProcessor" which just as to receive an item and send it out. So in this case, it is sure R equal W and ItemProcessor is not None.

But I don't manage to force the casting R to W. I think i could serialize and deserialize with serde, but it is a poor solution

Make processor be non-optional; in the case where it would be None, instead have the caller provide DefaultProcessor. That way, the types always fit and there are no complications.

If you want to be able to write code that does not have to specify DefaultProcessor, you can do that with a builder with generic parameters — the builder method that specifies a processor also changes the types.

1 Like

Hello,

My project is a batch processing toolkit. So we have:
ItemReader (R) -> ItemProcessor (R, W) -> ItemWriter (W).

So I delegate the business processing to the final user. He can implements his processor. And in some cases, there is not need to process (R = W).

So for the moment, in case of (R = W), I use serde cast W to R . When I will be stringer in Rust, I will improve my code.

Finally I did:

#[derive(Default)]
pub struct DefaultProcessor {}

impl<R: Serialize, W: DeserializeOwned> ItemProcessor<R, W> for DefaultProcessor {
    fn process<'a>(&'a self, item: &'a R) -> W {
        let serialised = serde_json::to_string(&item).unwrap();
        serde_json::from_str(&serialised).unwrap()
    }
}

Thanks again to all, your help is very important. I hope I will able to help the community later.

Simon