Cycle generic bounds when using associated types

Hi community,

I'm fiddling with c++ p2300 proposal in Rust and have an issue about cycling generic bounds.

In short, I have the following requirements:

  1. S is a sender with S::Output to specify it's output, while 'R' is a receiver with R::Value to specify it's input.
  2. Normally, S::Output should equal to R::Value to be able to pass the value from S to R
  3. But in this case, I need to add an F which is FnOnce between S and R to harmony their requirements. For example, if S::Output = String, F: FnOnce(String) -> i32 and R::Value = i32, then it's still workable.

Now comes the issue. Here I add one wrap receiver ThenReceiver which wraps F and R in order to apply F before passing to R, and S need to make sure it's output is compatible with R after applying the transform F

  1. In implementation of ThenReceiver, it makes sure the input I can be transformer by F: FnOnce(I) -> O, and can be fed into R with bound R: SetValue<Value = O>
pub trait SetValue {
    type Value;

    fn set_value(self, value: Self::Value);
}

pub struct ThenReceiver<I, F, R> {
    func: F,
    receiver: R,
    _phantom: PhantomData<I>,
}

impl<I, F, R, O> SetValue for ThenReceiver<I, F, R>
where
    F: FnOnce(I) -> O,
    R: SetValue<Value = O>,
{
    type Value = I;

    fn set_value(self, value: Self::Value) {
        self.receiver.set_value((self.func)(value));
    }
}
  1. Here is what I get the cycling issue whem compiling (exec-rs/then.rs at master · npuichigo/exec-rs · GitHub). Since ThenReceiver already takes over connecting F's output to R's input, it's quite straightforward that what I need to do here is specifying the input generic argument of ThenReceiver with S::Output. But the compile complains with cycling issues.
pub trait Sender<R> {
    type Output;
    type Operation: OperationState;

    fn connect(self, receiver: R) -> Self::Operation;
}

impl<S, F, R, O> Sender<R> for Then<S, F>
where
    S: Sender<ThenReceiver<S::Output, F, R>>,
    F: FnOnce(S::Output) -> O,
    R: SetValue<Value = O>,
{
    type Output = O;
    type Operation = ThenOperation<S::Operation>;

    fn connect(self, receiver: R) -> Self::Operation {
        ThenOperation {
            operation: self.sender.connect(ThenReceiver {
                func: self.func,
                receiver,
                _phantom: Default::default(),
            }),
        }
    }
}
error[E0391]: cycle detected when computing the bounds for type parameter `S`
  --> exec\src\adaptors\then.rs:49:28
   |
49 |     S: Sender<ThenReceiver<S::Output, F, R>>,
   |                            ^^^^^^^^^
   |
   = note: ...which immediately requires computing the bounds for type parameter `S` again

I think the idea is really simpile to express by traits and bounds, but it seems that I just cannot rely on S's associated types when adding bound to itself.

Really need your helps. Thanks!

It would be better to paste a playground link instead of code snippets like that, since the full code is short without relying on non-std crates.

You can have Then defined the way ThenReceiver is done: Rust Playground

pub struct Then<I, F, S> {
    func: F,
    sender: S,
    _phantom: PhantomData<I>,
}
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.