Rustc gives me trouble on a trait bound that is obviously valid

Compiler is nightly...

Im trying to make a change in my lib so it uses a generic trait Message instead of an associated type. However, I have a trait impl that no longer compiles.

I have a problem where I implement a generic trait on a generic struct. In the following code we can see that the trait does not know of parameter A. It only knows M and R, however the Receiver is generic over A. We can see from the global trait bounds that R == R, so if Message<()>, then A must impl Handler<M, ()>. However the send method, which should only be available for Message<()> (eg. no return value from handler), can not be implemented:

impl<A, M, R> Recipient<M, R> for Receiver<A>

   where A: Handler<M, R> ,
          M: Message<R>    ,
	       R: 'static       ,

{
	fn send( &mut self, msg: M ) -> Response< ThesRes<()> >

		where M: Message<()>,

	{
		self.addr.send( msg )
	}



	fn call( &mut self, msg: M ) -> Response< ThesRes<R> >
	{
		self.addr.call( msg )
	}

...

rustc complains:

error[E0277]: the trait bound `A: thespis::handler::Handler<_, ()>` is not satisfied
   --> src/single_thread/address.rs:247:13
    |
247 |         self.addr.send( msg )
    |                   ^^^^ the trait `thespis::handler::Handler<_, ()>` is not implemented for `A`

The type of self.addr obviously requires that before sending a message, the actor implements the right handler:

impl<A> Address<A> for Addr<A>

	where A: Actor,

{
   fn send<M>( &mut self, msg: M ) -> Response< ThesRes<()> >

	   where A: Handler<M, ()>,
            M: Message<()>,
...

This makes a whole lot of sense and I don't feel like it's a good idea to remove this trait bound.

So I tried to add the bound:

A: Handler<M, ()>

to the send method on Recipient, but that's not allowed, since it would make the implementation stricter than the trait:

error[E0276]: impl has stricter requirements than trait
   --> src/single_thread/address.rs:241:2
    |
241 |       fn send( &mut self, msg: M ) -> Response< ThesRes<()> >
    |  _____^
242 | |
243 | |         where A: Handler<M, ()>,
244 | |               M: Message<()>,
...   |
247 | |         self.addr.send( msg )
248 | |     }
    | |_____^ impl has extra requirement `A: thespis::handler::Handler<M, ()>`

The call to self.addr is obviously sound, because the impl is only valid for an A which has the same R in Handler<M, R> than Message<R>.

I would have thought that with chalkification and the constraint propagation of prolog, such simple deductions would be a piece of cake, but it seems not.

Is there a way to get around this, or do I start undoing everything and going back to an associated type?

Is this intended behavior or would it be worth filing an issue for this?

No, there's nothing that connects R = () here.

For the code in send, you have the impl constraint A: Handler<M, R> and the unified impl+fn constraint M: Message<R> + Message<()>. Note that M could very well implement Message for both independently, but this doesn't tell us anything about A.

It's too hard for me to suggest anything from these limited excerpts...

Of course, you are right... Doesn't look like it will be easy to solve this.

Just had a try with specialization, since a default impl would allow me not to implement all methods for R, and have a full impl for the () case, but it seems that that gives overflow evaluating the requirement single_thread::address::Receiver: thespis::recipient::Recipient<M, R>`` when trying to put a Receiver in a box dyn Recipient<M, R>.

I will probably have to split send and call functionality into two separate traits. That way impls can have the more specific constraints at trait level rather than putting them on a method only.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.