Get Rust to pick arbitrary applicable type?

I've got a trait that would be a #[marker] trait if that attribute were stable. In particular, its meaning doesn't depend on which impl is applicable, just that any impl is applicable. In order to avoid blanket impl conflicts, I use a "reason" parameter, denoted R:

trait Foo {}

trait Bar {}

trait Baz<R> {}

enum BecauseFoo {}

enum BecauseBar {}

impl<T: Foo> Baz<BecauseFoo> for T {}

impl<T: Bar> Baz<BecauseBar> for T {}

This works, but the usability is pretty bad, especially when more complex impls are added. In particular, whenever multiple impls are applicable, the user will get a type inference ambiguity error and they will be required to specify a particular "reason" in order to disambiguate and make the error go away:


impl Foo for () {}
impl Bar for () {}

fn takes_baz<T: Baz<R>, R>() {}

fn main() {
    takes_baz::<(), _>();
}

Here's the error:

error[E0283]: type annotations needed
  --> src/main.rs:21:5
   |
21 |     takes_baz::<(), _>();
   |     ^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `R` declared on the function `takes_baz`
   |
note: multiple `impl`s satisfying `(): Baz<_>` found
  --> src/main.rs:11:1
   |
11 | impl<T: Foo> Baz<BecauseFoo> for T {}
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12 |
13 | impl<T: Bar> Baz<BecauseBar> for T {}
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: required by a bound in `takes_baz`
  --> src/main.rs:15:17
   |
15 | fn takes_baz<T: Baz<R>, R>() {}
   |                 ^^^^^^ required by this bound in `takes_baz`

My question is: Is there any way to coax Rust into just picking one of the impls arbitrarily rather than complaining that it can't figure out which one to use?

It seems like a good case for the unstable auto_traits feature auto_traits - The Rust Unstable Book
Pin/Unpin as an example.

Not sure I follow how auto traits would help here?

I don't expect this to be possible. As a matter of principle, Rust doesn't want to let behavior of the program to silently change. Automatic ambiguity resolution would be sensitive to which traits are imported. It also seems very difficult to implement in a robust way given how complex type inference is.

2 Likes

Ah indeed I realized that might be an issue later. Because it’s not part of the prelude I don’t think it would have the same effect.
I tried my imagined proposal,but to no avail. l learned something along the way. ?Trait only works for Sized for relaxing trait bounds.
Also the part where it fell apart is that I was imagining an impl on a negative match but that also did not work. Sorry for the noise.

I was trying to do a

impl<T> Baz for T where T: !NotBaz {}

Which is not a thing. :man_facepalming:

However, what is the use case that you are trying to implement as that might provide for a better idea of an implementation idea.

Note that this syntax may become a thing in the future, but not with the meaning you probably expect it to (T: !Foo will hold only if T explicitly promise to never implement Foo by writing a impl !Foo for T)