Can't write non-overlapping blanket impls that involve associated type bindings
STR
Example from libcore:
#![crate_type = "lib"]
#![feature(associated_types)]
#![no_implicit_prelude]
trait Iterator {
type Item;
}
trait AdditiveIterator {
type Sum;
fn sum(self) -> Self::Sum;
}
impl<I> AdditiveIterator for I where I: Iterator<Item=u8> { //~error conflicting implementation
type Sum = u8;
fn sum(self) -> u8 { loop {} }
}
impl<I> AdditiveIterator for I where I: Iterator<Item=u16> { //~note conflicting implementation here
type Sum = u16;
fn sum(self) -> u16 { loop {} }
}
Version
7d4f487
No type can implement both Iterator<Item=u8> and Iterator<Item=u16>, therefore these blanket impls are non overlapping and should be accepted.
cc @nikomatsakis
I encountered this bug too, while trying to optimize
ToString(#18404):pub enum Void {} pub trait Display { // ... type AsStr = Void; fn fmt_as_str(&self) -> &<Self as Display>::AsStr { unimplemented!() } } pub trait ToString { fn to_string(&self) -> String; } impl<T: Display<AsStr=Void> + ?Sized> ToString for T { fn to_string(&self) -> String { format!("{}", self) } } impl<T: Display<AsStr=str> + ?Sized> ToString for T { fn to_string(&self) -> String { String::from_str(self.fmt_as_str()) } }This change could speed up
"".to_string()significantly (currently,String::from_stris between 2 and 5 times faster thanformat!) so I'd love to see this issue fixed.Chris Wong at 2015-01-29 23:24:24
cc me
Edward Wang at 2015-02-15 00:39:36
cc me
Jared Roesch at 2015-02-18 10:11:31
This is really causing problems for a crate I'm working on.
srrrse at 2015-05-07 02:10:40
Is this bug likely to get any love? I keep hitting this (see #23341) too. I had a look around by I can't really follow how overlapping impls are determined, I would have liked to have a go at it.
Cristi Cobzarenco at 2015-10-16 17:16:22
Any chance of this getting triaged any time soon? Also feeling pains from this issue.
Sage Griffin at 2016-01-18 21:00:45
I ran into this problem just now. I wanted to provide
and_then()andmap()methods for my type that would work ifTrait::TypeisOptionorResult.Martin Habovštiak at 2017-05-01 21:23:27
What's needed to advance this issue towards a fix? Are we dependent on Chalk?
Tyler J. Laing at 2018-08-13 20:42:25
Looks like you can kind of "trick" the compiler by using type specialization. On stable, 2015 edition:
https://play.rust-lang.org/?gist=d803059c64390d7182445bb21756f98c&version=stable&mode=debug&edition=2015
Tyler J. Laing at 2018-08-13 21:39:24
I'd like to add myself to the list of people that this causes problems for :).
Florian Gilcher at 2018-12-25 22:05:33
Looks like you can kind of "trick" the compiler by using type specialization. On stable, 2015 edition:
https://play.rust-lang.org/?gist=d803059c64390d7182445bb21756f98c&version=stable&mode=debug&edition=2015
@ZerothLaw could you explain your trick a bit? I think I'm running into this issue and am hoping for a workaround but I'm struggling to understand your example. If you could give some advice on how to apply that to the code in #58171 that would be great.
Daniel Beckwith at 2019-02-04 23:13:55
To give an update on this: In 2016, RFC 1672 was proposed to fix this, but was postponed until the Chalk integration is done. Additionally, according to https://github.com/rust-lang/rfcs/pull/1672#issuecomment-262152934, allowing these kinds of impls would allow users to express mutually exclusive traits, which is a very powerful feature that needs more in-depth consideration by the language team (hence marking as blocked on an RFC as well).
(also adding the keyword "disjoint" to make this issue easier to find)
Jonas Schievink at 2019-05-16 19:46:22
Triage: these kinds of impls are still not accepted yet.
Steve Klabnik at 2020-12-31 22:01:49
We wanted
FnMut(&char) -> boolto bePatternso you could usechar::is_ascii_digitbut this prevents that.Soni L. at 2021-03-08 02:20:48
There is also an associated
constversion of this where#![feature(associated_const_equality)] trait Foo { const ENABLED: bool; } trait Bar {} impl<T> Bar for T where T: Foo<ENABLED = true> {} impl<T> Bar for T where T: Foo<ENABLED = false> {}shouldn't conflict.
Harry Barber at 2023-03-10 12:35:05
Interestingly, it still fails with
-Z trait-solver=nextMax “Goldstein” Siling at 2023-04-12 19:48:11
This is possible on stable already, but not in a user friendly way.
Marcus Ofenhed at 2023-04-12 20:05:43
This crate enables you to write disjoint implementations in a straightforward way
Marin Veršić at 2023-09-23 12:42:57
@mversic This looks great! Would it be possible for the same thing to work with functions as well?
Example:
pub trait RouterExt { fn mount_route<E: ApiEndpoint<Auth = NoAuth>>(self, endpoint: E) -> Self; fn mount_route<E: ApiEndpoint<Auth = SomeAuth>>(self, endpoint: E) -> Self; }This would allow different functions to be called depending on the associated type of the endpoint. Is this possible? I went through the tests, but couldn't find a similar example
Rakshith Ravi at 2023-09-23 17:36:01
@mversic This looks great! Would it be possible for the same thing to work with functions as well?
no, you can't have two methods of the same name under one trait. I believe you should be able to do sth like this:
use disjoint_impls::disjoint_impls; trait ApiEndpointDispatch { type Auth; } enum NoAuth {} enum SomeAuth {} struct Endpoint1 {} impl ApiEndpointDispatch for Endpoint1 { type Auth = NoAuth; } struct Endpoint2 {} impl ApiEndpointDispatch for Endpoint2 { type Auth = SomeAuth; } disjoint_impls! { pub trait ApiEndpoint {} impl<E: ApiEndpointDispatch<Auth = NoAuth>> ApiEndpoint for E {} impl<E: ApiEndpointDispatch<Auth = SomeAuth>> ApiEndpoint for E {} } pub trait RouterExt { fn mount_route<E: ApiEndpoint>(self, endpoint: E) -> Self; }Marin Veršić at 2023-09-24 03:17:38
This seems to have been raised at the start of 2015 and here in October 2024 I get:
$cargo -V -v cargo 1.82.0 (8f40fc59f 2024-08-21) release: 1.82.0 commit-hash: 8f40fc59fb0c8df91c97405785197f3c630304ea commit-date: 2024-08-21 host: aarch64-apple-darwin libgit2: 1.8.1 (sys:0.19.0 vendored) libcurl: 8.7.1 (sys:0.4.74+curl-8.9.0 system ssl:(SecureTransport) LibreSSL/3.3.6) ssl: OpenSSL 1.1.1w 11 Sep 2023 os: Mac OS 15.0.1 [64-bit]
$cargo build Compiling nonover v0.1.0 (/Users/rod/code/rust/triage/nonover) error[E0119]: conflicting implementations of trait
AdditiveIterator--> src/lib.rs:21:1 | 15 | impl<I> AdditiveIterator for I where I: Iterator<Item=u8> { //~error conflicting implementation | --------------------------------------------------------- first implementation here ... 21 | impl<I> AdditiveIterator for I where I: Iterator<Item=u16> { //~note conflicting implementation here | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation For more information about this error, tryrustc --explain E0119. error: could not compilenonover(lib) due to 1 previous errorSo the "bug" or "lack of feature" remains after nearly 10 years. Is it time to decide to "fix" or say "not part of the language"?
Rod at 2024-10-19 18:49:06
For those expecting it to work with the new trait solver automatically, here's a reminder that it's blocked on an RFC as it unlocks mutually exclusive traits.
QuineDot at 2024-11-29 18:24:19