Skip to content

Need negative trait bound #42721

Open
Open

Description

@svmk
use std::convert::From;

struct SomeError;
enum Error<E> {
    One(SomeError),
    Two(E),
}

/// impl<E> From<SomeError> for Error<E> where E: !SomeError {
impl<E> From<E> for Error<E> {
    fn from(error: E) -> Self {
        Error::One(error)
    }
}
impl<E> From<SomeError> for Error<E> {
    fn from(error: E) -> Self {
        Error::Two(error)
    }
}

It's produces error:

rustc 1.18.0 (03fc9d622 2017-06-06)
error: main function not found

error[E0119]: conflicting implementations of trait `std::convert::From<SomeError>` for type `Error<SomeError>`:
  --> <anon>:15:1
   |
9  | / impl<E> From<E> for Error<E> {
10 | |     fn from(error: E) -> Self {
11 | |         Error::One(error)
12 | |     }
13 | | }
   | |_- first implementation hereadd
14 | 
15 | / impl<E> From<SomeError> for Error<E> {
16 | |     fn from(error: E) -> Self {
17 | |         Error::Two(error)
18 | |     }
19 | | }
   | |_^ conflicting implementation for `Error<SomeError>`

error: aborting due to previous error

May'be shall implement contruction:

impl<E> From<SomeError> for Error<E> where E: !SomeError {
  ...
}

Activity

  1. oli-obk commented on Jun 17, 2017

    @oli-obk
    Contributor

    Negative bounds have a lot of issues. Like the fact that implementing a trait becomes a breaking change. There's some related discussion in rust-lang/rfcs#1834 . There's probably more discussion in the internals forum.

  2. oli-obk commented on Jun 17, 2017

    @oli-obk
    Contributor

    Oh and you might want to have a look at specialization. I think it allows your case without the Pandora's box of issues that negative trait bounds brings with it.

  3. soltanmm commented on Aug 14, 2017

    @soltanmm

    AFAICT From's blanket implementation without use of the default keyword precludes specialization at the current time, unfortunately. An alternative formulation is the OIBIT trick for negative reasoning (someone mentioned this somewhere a while back; I don't remember who or where):

    #![feature(optin_builtin_traits)]
    
    trait NotEq {}
    impl NotEq for .. {}
    impl<X> !NotEq for (X, X) {}
    
    struct X<A>(A);
    impl<A, B> From<X<B>> for X<A>
        where
            A: From<B>,
            (A, B): NotEq,
    {
        fn from(a: X<B>) -> Self { X(a.0.into()) }
    }
    
    fn main() {}
  4. kdy1 commented on Nov 27, 2017

    @kdy1
    Contributor

    Does that works for a struct with (u8, u8)? Considering how oibit (Send/Sync) works, I don't think it would..

  5. axos88 commented on Jul 22, 2018

    @axos88

    +1

    I'm having trouble with implementing Debug for structs that have fields which are not Debug:

    enum MyOption<T> {
      None,
      Some(T)
    }
    
    impl<T> Debug for MyOption<T> where T: ?Debug {
              match self {
                None => write!(f, "Unset"),
                Some(_) => write!(f, "An unprintable value")         
            }
    }
     
    #[derive(Debug)]
    struct Foo {
      a: u32,
      b: MyOption<SomethingWithoutDebug>
    }
    

    Another solution for my case would be if such a pattern could be implemented:

      self match {
        None => ...
        Some(x: T where T: Debug) => ...
        Some(_) => ...
    
  6. kennytm commented on Jul 22, 2018

    @kennytm
    Member

    @axos88 This is already supported through specialization.

    #![feature(specialization)]
    
    use std::fmt::{self, Debug};
    
    enum MyOption<T> {
      None,
      Some(T)
    }
    
    impl<T> Debug for MyOption<T> {
        default fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            match self {
                MyOption::None => write!(f, "Unset"),
                MyOption::Some(_) => write!(f, "An unprintable value"),
            }
        }
    }
    
    impl<T: Debug> Debug for MyOption<T> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            match self {
                MyOption::None => write!(f, "Unset"),
                MyOption::Some(t) => t.fmt(f),
            }
        }
    }
    
    struct Unprintable;
    
    fn main() {
        println!("{:?}", MyOption::Some(1234));
        println!("{:?}", MyOption::Some(Unprintable));
        println!("{:?}", MyOption::None::<Unprintable>);
    }
  7. stepancheg commented on Sep 14, 2019

    @stepancheg
    Contributor

    @kennethbgoodin more complicated use cases are not possible with trait specialization.

    For example:

    #![feature(specialization)]
    
    trait Integer {
        fn to_int(&self) -> i32;
    }
    
    trait Collection {
        fn to_vec(&self) -> Vec<i32>;
    }
    
    trait PrintAnything {
        fn print(&self) -> String;
    }
    
    impl<T> PrintAnything for T {
        default fn print(&self) -> String {
            format!("unprintable")
        }
    }
    
    impl<T: Integer> PrintAnything for T {
        fn print(&self) -> String {
            format!("int {}", self.to_int())
        }
    }
    
    impl<T: Collection> PrintAnything for T {
        fn print(&self) -> String {
            format!("collection {:?}", self.to_vec())
        }
    }
    error[E0119]: conflicting implementations of trait `PrintAnything`:
      --> src/main.rs:27:1
       |
    21 | impl<T: Integer> PrintAnything for T {
       | ------------------------------------ first implementation here
    ...
    27 | impl<T: Collection> PrintAnything for T {
       | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
    
    error: aborting due to previous error
    
  8. kennytm commented on Sep 14, 2019

    @kennytm
    Member

    It is possible if specialization is enhanced to recognize intersection (previously attempted in #49624) i.e.

    impl<T> PrintAnything for T { ... }
    impl<T: Integer> PrintAnything for T { ... }
    impl<T: Collection> PrintAnything for T { ... }
    impl<T: Integer + Collection> PrintAnything for T { ... }

    (If you need to specialize to N traits like these you'll need 2N impls)

  9. axos88 commented on Sep 14, 2019

    @axos88

    @kennytm That doesn't seem like a solution for anything with N >= 4...

  10. quark-zju commented on Apr 12, 2020

    @quark-zju
    Contributor

    Would a subset of negative bounds be less problematic? For example, !Sized? That alone might already solve some problems.

  11. Ixrec commented on Apr 17, 2020

    @Ixrec
    Contributor

    #68004 should probably be mentioned here. It's obviously not a complete negative bounds feature, and the motivation is largely unrelated, but it's a step in this direction.

  12. tib888 commented on May 9, 2020

    @tib888

    error[E0119]: conflicting implementations of trait PrintAnything:
    --> src/main.rs:27:1
    |
    21 | impl<T: Integer> PrintAnything for T {
    | ------------------------------------ first implementation here
    ...
    27 | impl<T: Collection> PrintAnything for T {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation

    error: aborting due to previous error

    I have run into pretty much the same situation.
    "conflicting implementation" check should consider the type constraints!

  13. newpavlov commented on Jul 7, 2020

    @newpavlov
    Contributor

    Another example which requires negative trait bounds:

    trait Foo { .. }
    trait Bar { .. }
    impl<T: Foo> Bar for T { .. }
    impl<T: Foo> Foo for &T { .. }
    impl<T: Bar> Bar for &T { .. }

    I am not sure if specialization can help here.

  14. tvallotton commented on Jun 23, 2021

    @tvallotton
    Contributor

    Negative bounds have a lot of issues. Like the fact that implementing a trait becomes a breaking change. There's some related discussion in rust-lang/rfcs#1834 . There's probably more discussion in the internals forum.

    This could be address by mandating complementary implementations. So if you want to write something like

    impl<T: !Foo> Bar for T { \* ... *\ }

    you'd be forced to implement

    impl<T: Foo> Bar for T { \* ... *\ }

    Therefore implementing Foo cannot break code because T has to implement Bar regardless of whether it implements T. I explained this more thoroughly here.

  15. JonahPlusPlus commented on Mar 2, 2025

    @JonahPlusPlus

    I have a use-case where this would be really helpful:
    I'm making some changes to trie-rs, a trie structure crate with two APIs: one that is map-like (Trie<Token, Value>) and another that is set-like (Trie<Token>).
    The set trie is simply a newtype of the map trie (Trie<Token, ()>) that removes methods for accessing values (like HashSet is a newtype of HashMap in the standard library).

    It would be nice to remove the newtype and simply make the set trie a type alias of the map trie by using negative bounds to disable methods for sets.

    e.g.

    trait Unit {}
    
    impl Unit for () {}
    
    impl<Token, Value> Trie<Token, Value> {
      fn get_value(&self, label: impl Label<Token>) -> Option<&Value>
      where Value: !Unit
      {
        // ...
      }
    }

    I also have NodeRef and KeyRef types that could be merged with the same pattern.

    Alternatives:

    • General auto traits (Tracking issue for auto traits #13231) (i.e. auto trait NotUnit {} and impl !NotUnit for () {} so where Value: NotUnit)
    • Auto traits for ZST and non-ZST types (i.e. where Value: NotZst)
    • Specialization with associated marker types (i.e. where Value: Size<Kind = NonZero> 😬)
    • Concrete negative bounds (IDK what it's called) (i.e. where Value != ())

    But negative trait bounds was what first came naturally to me, plus the idea of a trait that is minimally implemented is aesthetically appealing to me (unlike auto traits which get implemented for everything).

  16. porky11 commented on Mar 4, 2025

    @porky11

    @JonahPlusPlus
    This looks like bad design to me.

    Why should it be forbidden to call get_value ONLY if the if the value is ()?
    There isn't a good reason for it.

    Maybe you could make it a crate internal type or something, which you don't expose to public, so nobody would be able to create a Trie<Token, CrateInternalType> directly, and maybe you could ensure that no user would get a reference to such a type. This way you could just not call get_value for this type.
    But not sure if something like this would work here.

  17. JonahPlusPlus commented on Mar 4, 2025

    @JonahPlusPlus

    @porky11 Yeah, it could use an internal type, but the point is that it would still need negative bounds or some alternative in order to merge the set and map variants.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-specializationArea: Trait impl specializationA-trait-systemArea: Trait systemC-feature-requestCategory: A feature request, i.e: not implemented / a PR.T-langRelevant to the language team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions