Is it possible to compare associated constants on trait implementors?

I just tried that. It doesn't seem to work. MRE below:

struct Foo;
struct Bar;

trait Spamm {
    const EGGS: u8;
}

impl Spamm for Foo {
    const EGGS: u8 = 123;
}

impl Spamm for Bar {
    const EGGS: u8 = 42;
}

fn frobnicate<A, B>(a: A, b: B)
where
    A: Spamm,
    B: Spamm<EGGS = <A as Spamm>::EGGS>,
{
    todo!()
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0575]: expected associated type, found associated constant `Spamm::EGGS`
  --> src/lib.rs:19:21
   |
19 |     B: Spamm<EGGS = <A as Spamm>::EGGS>,
   |                     ^^^^^^^^^^^^^^^^^^ not a associated type

error: expected constant, found type
  --> src/lib.rs:19:21
   |
19 |     B: Spamm<EGGS = <A as Spamm>::EGGS>,
   |              ----   ^^^^^^^^^^^^^^^^^^ unexpected type
   |              |
   |              expected a constant because of this associated constant
   |
note: the associated constant is defined here
  --> src/lib.rs:5:5
   |
5  |     const EGGS: u8;
   |     ^^^^^^^^^^^^^^

warning: unused variable: `a`
  --> src/lib.rs:16:21
   |
16 | fn frobnicate<A, B>(a: A, b: B)
   |                     ^ help: if this is intentional, prefix it with an underscore: `_a`
   |
   = note: `#[warn(unused_variables)]` on by default

warning: unused variable: `b`
  --> src/lib.rs:16:27
   |
16 | fn frobnicate<A, B>(a: A, b: B)
   |                           ^ help: if this is intentional, prefix it with an underscore: `_b`

For more information about this error, try `rustc --explain E0575`.
warning: `playground` (lib) generated 2 warnings
error: could not compile `playground` (lib) due to 2 previous errors; 2 warnings emitted

If we correct this to use a const expression (in braces), we get a better error message:

            B: Spamm<EGGS = {<A as Spamm>::EGGS>}>,
error[E0658]: associated const equality is incomplete
  --> src/lib.rs:40:22
   |
39 |             B: Spamm<EGGS = { <A as Spamm>::EGGS }>,
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: see issue #92827 <https://github.com/rust-lang/rust/issues/92827> for more information

So this feature is incomplete. There is probably more info at the issue mentioned.

1 Like

Tried an alternative "spelling" of the same idea, didn't work either

If you just want to check that they match at compile time, you can do something like

const { assert!(A::EGGS == B::EGGS); }
4 Likes

Alas, this does not work in my special case:

    /// Communicate with the NCP.
    fn communicate<C, R>(&mut self, command: C) -> impl Future<Output = Result<R, Error>> + Send
    where
        C: Parameter + ToLeStream,
        R: Clone + Debug + Parameter<Id = <C as Parameter>::Id> + FromLeStream,
        <Extended<Command> as ValidControl>::Size: From<<C as Parameter>::Id>,
        <Extended<Response> as ValidControl>::Size: From<<R as Parameter>::Id>,
    {
        const {
            assert_eq!(C::ID, R::ID);
        }

        async {
            self.send::<Extended<Command>, C>(command).await?;
            self.receive::<Extended<Response>, R>().await
        }
    }
error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability
  --> src/transport.rs:54:13
   |
54 |             assert_eq!(C::ID, R::ID);
   |             ^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: see issue #80384 <https://github.com/rust-lang/rust/issues/80384> for more information
   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0015]: cannot call non-const operator in constants
  --> src/transport.rs:54:13
   |
54 |             assert_eq!(C::ID, R::ID);
   |             ^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: calls in constants are limited to constant functions, tuple structs and tuple variants
   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0015]: cannot call non-const fn `assert_failed::<<C as Parameter>::Id, <C as Parameter>::Id>` in constants
  --> src/transport.rs:54:13
   |
54 |             assert_eq!(C::ID, R::ID);
   |             ^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: calls in constants are limited to constant functions, tuple structs and tuple variants
   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)

As a workaround, I currently just issue a warning at runtime.
If the library user does something like that, they're on their own anyways as per the docs.

    fn communicate<C, R>(&mut self, command: C) -> impl Future<Output = Result<R, Error>> + Send
    where
        C: Parameter + ToLeStream,
        R: Clone + Debug + Parameter + FromLeStream,
        <Extended<Command> as ValidControl>::Size: From<<C as Parameter>::Id>,
        <Extended<Response> as ValidControl>::Size: From<<R as Parameter>::Id>,
    {
        if <Extended<Command> as ValidControl>::Size::from(C::ID)
            != <Extended<Response> as ValidControl>::Size::from(R::ID)
        {
            warn!("Command and response IDs do not match.");
        }

        async {
            self.send::<Extended<Command>, C>(command).await?;
            self.receive::<Extended<Response>, R>().await
        }
    }

I believe you have to use assert! not assert_eq!.

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.