Skip to content

Specialization causes confilicting implementations with T and Option<T> #41140

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
afonso360 opened this issue Apr 7, 2017 · 5 comments
Closed

Comments

@afonso360
Copy link
Contributor

Trying to implement a specialization on type T of Option<T> causes the error E0119 stating that multiple implementations exist.

#![feature(specialization)]

use std::fmt;

trait ToFlag {
    fn format_flag(self, flag: char) -> String;
}

impl<T> ToFlag for T 
    where T: fmt::Display {
    default fn format_flag(self, flag: char) -> String {
        format!("-{} {}", flag, self)
    }
}

impl<T> ToFlag for Option<T> where T: fmt::Display {
    fn format_flag(self, flag: char) -> String {
        match self {
            Some(s) => s.format_flag(flag),
            None => "".to_string(),
        }
    }
}


fn main() {
    println!("std: {}", 1u32.format_flag('g'));
    println!("opt: {}", Some(1u32).format_flag('g'));
    println!("non: {}", None.format_flag('g'));
}

Expected output

std: -g 1
opt: -g 1
non: 

Actual output

The program fails to compile with

error[E0119]: conflicting implementations of trait `ToFlag` for type `std::option::Option<_>`:
  --> <anon>:16:1
   |
9  |   impl<T> ToFlag for T 
   |  _- starting here...
10 | |     where T: fmt::Display {
11 | |     default fn format_flag(self, flag: char) -> String {
12 | |         format!("-{} {}", flag, self)
13 | |     }
14 | | }
   | |_- ...ending here: first implementation here
15 | 
16 |   impl<T> ToFlag for Option<T> where T: fmt::Display {
   |  _^ starting here...
17 | |     fn format_flag(self, flag: char) -> String {
18 | |         match self {
19 | |             Some(s) => s.format_flag(flag),
20 | |             None => "".to_string(),
21 | |         }
22 | |     }
23 | | }
   | |_^ ...ending here: conflicting implementation for `std::option::Option<_>`

error: aborting due to previous error


Version Info

$ rustc --version --verbose
rustc 1.18.0-nightly (5e122f59b 2017-04-01)
binary: rustc
commit-hash: 5e122f59ba23494d460466cca53c71646d99c767
commit-date: 2017-04-01
host: x86_64-unknown-linux-gnu
release: 1.18.0-nightly
LLVM version: 3.9

Other Notes

This can potentially be a duplicate of #36587

playpen

@bluss
Copy link
Member

bluss commented Apr 8, 2017

Hi! Option<T> doesn't implement Display, so the second impl is not a specialization of the first (because the second does not apply to a more specific subset of the first). In that sense, specialization does not apply and the same old trait impl coherence rules are setting the limits here.

@afonso360
Copy link
Contributor Author

Hi, I might be confused about this, I thought specialization applied whenever you had more type info than the default type which in this case is T, the second impl has more type info Option<T> thus specialization should apply, correct? Even though it is still a generic it is a more specific subset of T?

I went back a read trough the RFC for specialization and it has a section on Overlap which is what I think might apply here, specifically this section:

More constraints lead to more specific impls, e.g.:

T: Clone is more specific than T
Bar for T is more specific than Bar for U

In this situation I have an extra constraint on the generic type.

I didn't read the discussion on the Specialization PR so if an ammendment was made I might be wrong.

I might also have this wrong, but I don't require that Option implements fmt::Display the constraint is that the wrapped type implements it. Or, because I require the wrapped type to impl the Display trait I also require that Option impl's that trait?

@bluss
Copy link
Member

bluss commented Apr 15, 2017

Hi, I might be confused about this, I thought specialization applied whenever you had more type info than the default type which in this case is T, the second impl has more type info Option thus specialization should apply, correct?

Since the feature is not finished, I don't really know. My understanding (as in the previous comment) is not like this. I'd bring this discussion to the tracking issue for specialization. It could be a request for clarification of the rules, or a request to widen the rules so that your code is allowed.

@withoutboats
Copy link
Contributor

withoutboats commented Apr 21, 2017

Hi, I might be confused about this, I thought specialization applied whenever you had more type info than the default type which in this case is T, the second impl has more type info Option thus specialization should apply, correct? Even though it is still a generic it is a more specific subset of T?

@bluss is right. Option<T: Display> is not a subset of T: Display, because Option<T: Display> does not implement Display.

The first impl is the set of types that implement Display, and the second impl is the set of Option<T> where T: Display. The second set is not a subset of the first, because not all of those Option<T> types (in fact none of them) implement Display.

Option<T> is a subset of T, because there are no bounds in play. And some traits work: Option<T: Clone> is a subset of T: Clone, but this is only because there is an impl Clone for Option<T: Clone>. Meanwhile, Rc<T> is a subset of T: Clone, even if T doesn't impl Clone, because the impl of Clone for Rc doesn't care if the T is Clone.

We may allow this someday by changing the specialization rules to be a bit more complex than the 'subset' rule, but this is the subset rule.

@withoutboats
Copy link
Contributor

Closing this because it isn't a bug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants