rustassociated-types

Constraint associated type of a generic associated type


I have a type Builder with a Generic Associated Type (GAT) InstanceForBuilder<'a>. I'd like to write a function (build_with_42_for_bool<Builder>) that constraints the Builder to only those cases where Builder::InstanceForBuilder<'a>::InstanceProperty == bool (for all 'a).

I've been playing around for a while to get the syntax around this for <'a> right, but haven't been able to make this work.

The lifetime can't be a template argument of the function itself, because the reference only lives inside it.

Is this possible at all yet, given that GAT is an unstable feature?

playground

#![feature(generic_associated_types)]

// Trait definitions.

trait Builder {
    type InstanceForBuilder<'a>: Instance<'a>;

    fn build<'a>(&self, val: &'a usize) -> Self::InstanceForBuilder<'a>;
}

trait Instance<'a> {
    // Some functions will only work when the instance has some concrete associated type.
    type InstanceProperty;
}

fn build_with_42_for_bool<B: Builder>(builder: B)
where
    // TODO: What do I put here to make this work?
    for<'a> B::InstanceForBuilder<'a>: Instance<'a, InstanceProperty = bool>,
{
    builder.build(&42);
    // Do some testing here. The Instance won't be returned.
}

// Now try it out.

struct MyBuilder;
struct MyInstance<'a> {
    val: &'a usize,
}

impl Builder for MyBuilder {
    type InstanceForBuilder<'a> = MyInstance<'a>;

    fn build<'a>(&self, val: &'a usize) -> Self::InstanceForBuilder<'a> {
        MyInstance { val }
    }
}

impl<'a> Instance<'a> for MyInstance<'a> {
    type InstanceProperty = bool;
}

fn main() {
    let builder = MyBuilder;
    build_with_42_for_bool(builder); // TODO: Doesn't work
}

In my actual code, build_with_42_for_bool is a helper for testing that constructs the arguments passed to build in a specific way. For now I'll probably just inline that function everywhere, since the only problem is how to specify the lifetime of this one function. The code itself works fine.

Here's the full error:

Compiling pairwise-aligner v0.1.0 (/home/philae/git/eth/git/astar-pairwise-aligner)
error[E0271]: type mismatch resolving `for<'a> <<_ as Builder>::InstanceForBuilder<'a> as Instance<'a>>::InstanceProperty == bool`
  --> examples/test.rs:45:5
   |
45 |     build_with_42(builder); // TODO: Doesn't work
   |     ^^^^^^^^^^^^^ expected `bool`, found associated type
   |
   = note:         expected type `bool`
           found associated type `<<_ as Builder>::InstanceForBuilder<'_> as Instance<'_>>::InstanceProperty`
   = help: consider constraining the associated type `<<_ as Builder>::InstanceForBuilder<'_> as Instance<'_>>::InstanceProperty` to `bool`
   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
note: required by a bound in `build_with_42`
  --> examples/test.rs:19:53
   |
16 | fn build_with_42<B: Builder>(builder: B)
   |    ------------- required by a bound in this
...
19 |     for<'a> B::InstanceForBuilder<'a>: Instance<'a, InstanceProperty = bool>,
   |                                                     ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `build_with_42`

error[E0271]: type mismatch resolving `for<'a> <MyBuilder as Builder>::InstanceForBuilder<'a> == <MyBuilder as Builder>::InstanceForBuilder<'a>`
  --> examples/test.rs:45:5
   |
45 |     build_with_42(builder); // TODO: Doesn't work
   |     ^^^^^^^^^^^^^ type mismatch resolving `for<'a> <MyBuilder as Builder>::InstanceForBuilder<'a> == <MyBuilder as Builder>::InstanceForBuilder<'a>`
   |
note: expected this to be `<MyBuilder as Builder>::InstanceForBuilder<'a>`
  --> examples/test.rs:32:35
   |
32 |     type InstanceForBuilder<'a> = MyInstance<'a>;
   |                                   ^^^^^^^^^^^^^^
   = note: expected associated type `<MyBuilder as Builder>::InstanceForBuilder<'a>`
                       found struct `MyInstance<'a>`
help: a method is available that returns `<MyBuilder as Builder>::InstanceForBuilder<'a>`
  --> examples/test.rs:8:5
   |
8  |     fn build<'a>(&self, val: &'a usize) -> Self::InstanceForBuilder<'a>;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ consider calling `Builder::build`
note: required by a bound in `build_with_42`
  --> examples/test.rs:19:40
   |
16 | fn build_with_42<B: Builder>(builder: B)
   |    ------------- required by a bound in this
...
19 |     for<'a> B::InstanceForBuilder<'a>: Instance<'a, InstanceProperty = bool>,
   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `build_with_42`

For more information about this error, try `rustc --explain E0271`.
error: could not compile `pairwise-aligner` due to 2 previous errors

Solution

  • The answer from DreamConspiracy is a little outdated. I'd like to reuse the example codes from him to show a compiled solution

    Instead of

    where B::InstanceForBuilder::InstanceProperty = bool
    

    You should write

    fn build_with_42_for_bool_instance<'a, B, I>(builder: B)
    where
        B : Builder<InstanceForBuilder<'a>=I>,
        I : Instance<'a, InstanceProperty=bool>,
    {
        builder.build(&42);
    }