Cannot understand why E0277 is triggered here:


#1

Along the lines of my question on deref coercion:

I’d like to understand why the following code doesn’t work:

pub struct TTestSyncClient {
  i_prot: Rc<RefCell<Box<TProtocol>>>,
  o_prot: Rc<RefCell<Box<TProtocol>>>,
  sequence_number: i32,
}

impl TTestSyncClient {
  fn send_testVoid(&mut self) -> rift::Result<()> {
    self.sequence_number = self.sequence_number + 1;
    let message_ident = TMessageIdentifier { name:"testVoid".to_owned(), message_type: TMessageType::Call, sequence_number: self.sequence_number };
    let call_args = testVoid_args {  };
    try!(self.o_prot.borrow_mut().write_message_begin(&message_ident));
    try!(call_args.write_to_out_protocol(&mut self.o_prot.borrow_mut())); // <-- ERROR HERE
    try!(self.o_prot.borrow_mut().write_message_end());
    Ok(())
  }
  fn recv_testVoid(&mut self) -> rift::Result<()> {
    let message_ident = try!(self.i_prot.borrow_mut().read_message_begin());
    try!(verify_expected_sequence_number(self.sequence_number, message_ident.sequence_number));
    try!(verify_expected_service_call("testVoid", &message_ident.name));
    try!(verify_expected_message_type(TMessageType::Reply, message_ident.message_type));
    let result = try!(testVoid_result::read_from_in_protocol(&mut self.i_prot.borrow_mut())); // <-- ERROR HERE
    try!(self.i_prot.borrow_mut().read_message_end());
    result.ok_or()
  }
}

#[derive(Debug)]
struct testVoid_args {
}

impl testVoid_args {
  fn read_from_in_protocol<P: TProtocol>(i_prot: &mut P) -> rift::Result<testVoid_args> {
    try!(i_prot.read_struct_begin());
    loop {
      let field_ident = try!(i_prot.read_field_begin());
      if field_ident.field_type == TType::Stop {
        break;
      }
      let field_id = try!(field_id(&field_ident));
      match field_id {
        _ => {
          try!(i_prot.skip(field_ident.field_type));
        },
      }
    }
    try!(i_prot.read_struct_end());
    let ret = testVoid_args {};
    Ok(ret)
  }
  fn write_to_out_protocol<P: TProtocol>(&self, o_prot: &mut P) -> rift::Result<()> {
    let struct_ident = TStructIdentifier { name: "testVoid_args".to_owned() };
    try!(o_prot.write_struct_begin(&struct_ident));
    try!(o_prot.write_field_stop());
    try!(o_prot.write_struct_end());
    try!(o_prot.flush());
    Ok(())
  }
}

Spitting out the error:

/*
error[E0277]: the trait bound `std::cell::RefMut<'_, Box<rift::protocol::TProtocol + 'static>>: rift::protocol::TProtocol` is not satisfied
    --> src/rift_test.rs:2313:20
     |
2313 |     try!(call_args.write_to_out_protocol(&mut self.o_prot.borrow_mut()));
     |                    ^^^^^^^^^^^^^^^^^^^^^ trait `std::cell::RefMut<'_, Box<rift::protocol::TProtocol + 'static>>: rift::protocol::TProtocol` not satisfied
error[E0277]: the trait bound `std::cell::RefMut<'_, Box<rift::protocol::TProtocol + 'static>>: rift::protocol::TProtocol` is not satisfied
    --> src/rift_test.rs:2322:23
     |
2322 |     let result = try!(testVoid_result::read_from_in_protocol(&mut self.i_prot.borrow_mut()));
     |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `std::cell::RefMut<'_, Box<rift::protocol::TProtocol + 'static>>: rift::protocol::TProtocol` not satisfied
     |
     = note: required by `rift_test::testVoid_result::read_from_in_protocol`
*/

The message seems to imply that the RefMut<Box>> is not being derefed, and I don’t understand why.


#2

Note that a helpful soul on IRC showed me how to work around the error (example here), but neither of us understands why the caller has to explicitly dereference twice.


#3

The message seems to imply that the RefMut<Box>> is not being derefed, and I don’t understand why.

Correct. It’s not being derefed, because there’s nothing to cause it to be derefed. The compiler only derefs in two situations: when it’s looking for a field/member via dot member access, and when trying to coerce a borrowed pointer. That’s it.

Note that neither of those apply here: the function you’re calling wants a &mut T, and you’re giving it a &mut T. The problem is that RefMut<_> doesn’t satisfy the constraint RefMut<_>: TProtocol, hence the error.

To put it another way: Rust won’t deref something to try and satisfy constraints.


#4

This one snagged me too a while back. Here’s a simpler case:

use std::fmt::Debug;

struct S(i32);
impl std::ops::Deref for S {
    type Target = i32;
    fn deref(&self) -> &i32 { &self.0 }
}

fn concrete(t: &i32)         { println!("{:?}", t); }
fn variable<T: Debug>(t: &T) { println!("{:?}", t); }
fn object(t: &Debug)         { println!("{:?}", t); }

#[test]
fn no_coercion_for_bounds() {
    let s = S(10);
    concrete(&s);

    // error[E0277]: the trait bound `S: std::fmt::Debug` is not satisfied
    // variable(&s);
    // object(&s);
    
    // These work fine, because the `as` expression forces
    // the `Deref` coercions. Then `T: Debug` is satisfied when
    // T is i32, and coercion to a trait object &Debug works for &i32.
    variable(&s as &i32);
    object(&s as &i32);
}

The call to concrete works just fine, because Rust is trying to coerce a &S to a &i32, so it tries the Deref coercions.

The first call to variable fails, because Rust matches &S with &T, says, “Okay, the type variable T must be S,” and then says "S doesn’t implement Debug, fail". Since the two concrete types &S and &i32 never get paired up, it never tries to apply Deref coercions.

I believe the first call to object fails for similar reasons: coercion to a trait object &Tr requires a reference of type &T for some T: Tr; again, the constraint isn’t satisfied.

That phrasing has been the most helpful way for me to think about it.


#5

@jimb That example - and your description - was super helpful. Thank you.

And yes, @DanielKeep’s statement below is the most helpful way to think about this.

To put it another way: Rust won’t deref something to try and satisfy constraints.

Thanks guys!