[SOLVED] Help: Compiler couldn't resolve opaque type!

Hello There :wave:,

I used from compiler to give a meaningful and pretty helpful error messages, But recently i faced this.

error[E0308]: mismatched types
        --> src/identity_key.rs:80:19
       |
    80 |       public_key: pub_key,
       |                   ^^^^^^^ expected opaque type, found a different opaque type
       |
        = note: expected type `impl ecc::ECKey` (opaque type)
                   found type `impl ecc::ECKey` (opaque type)

which left me wondering, what is just happened ?

Here is a little bit information about what kind of project that i'm working on:

I'm working on porting the libsignal-protocol-java from java to pure rust.

Here is the repo libsignal-protocol-rs the pure-rust brach.
the master branch is just a wrapper around the libsignal-protocol-c which is fine, but it has a lot of unsafe code.

anyway, here is a little snippet from where the errors happens

// i have this trait here, an abstraction over the Keys
trait ECKey {
  fn serialize(&self) -> Vec<u8>;
  fn get_type(&self) -> u8;
  fn get_key(&self) -> Vec<u8>;
}

and here is a simplified version of it's usage and where is the error happens !.

/// Holder for public and private identity key pair.
pub struct IdentityKeyPair<E: ECKey> {
  public_key: E,
  private_key: E,
}

impl<E: ECKey> IdentityKeyPair<E> {
  pub fn new(public_key: E, private_key: E) -> IdentityKeyPair<E> {
    IdentityKeyPair {
      public_key,
      private_key,
    }
  }

  pub fn from_raw(serialized: &[u8]) -> IdentityKeyPair<impl ECKey> {
    // ... some code...
   // ignore the `public_key` and `private_key` for now, they are a `Vec<u8>`
    let pub_key = Curve::decode_point(&public_key, 0)?; // -> impl ECKey
    let prv_key = Curve::decode_private_point(&private_key); // -> impl ECKey
    let pair = IdentityKeyPair {
      private_key: prv_key, // this ok here
      public_key: pub_key, // but this a error in compile
    };
    pair
  }
}

For the full code see here.

Thank you :slight_smile:

If you want to mix different types, you need dyn Trait, or in your case you could have two generic parameters allowing two different types.

impl Type is not a type abstraction. It's still a unique type, you just don't need to write out by hand the full type name.

1 Like

oh, so i need a Box<dyn Trait> i know it, but i thought that rust treat the impl Trait as an Abstraction, maybe i then could fix this by:

struct IdentityKeyPair<E: ECKey, K: ECKey> { public_key: E, private_key: K }

but that is not the problem now, the actual issue is the the compiler error message is misleading maybe, not really helpful !

and thank you for your help :smile:

1 Like

I realize your question is more about the error message (clarity, or lack thereof), but perhaps your Curve::decode_... fns should specify that they return the DjbECKey type (which they do). AFAICT, DjbECKey is a publicly exported type of your crate. I'm also unsure whether IdentityKeyPair should allow two different ECKey types.

You may have good reason to hide the concrete return type, but just wanted to mention this.

2 Likes

Yes, this error message should be better:

https://github.com/rust-lang/rust/issues/57266

2 Likes

You alright, I'm maybe need to think about other way around, i was trying to be as close as to the java source, but it seems it would complete things instead of making it simple, I'll try instead of this using a well known types or maybe use Box<dyn Trait> it would solve these abstraction problems.

This approach tends to not work very well when applied to porting Java's implementation details to Rust. Instead, port the semantics/functionality but try to write it in more Rust friendly/idiomatic manner.

Box<dyn ECKey> may have meaningful performance degradation, so be sure you're using a trait object only if you actually need that rather than, as mentioned, carrying over Java impl details.

3 Likes

Yeah, you alright, i should instead of sticking with java, i should be more ergonomic to rust.

Thanks again for your help :grinning:

Yeah, no problem. I've been tempted into the same path myself (i.e. "copying" Java impl to Rust), and know first-hand that it usually ends in tears :slight_smile:.

2 Likes

Same my friend, same :joy: