Skip to content
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

Invalid collision with TryFrom implementation? #50133

Open
npmccallum opened this issue Apr 21, 2018 · 28 comments
Open

Invalid collision with TryFrom implementation? #50133

npmccallum opened this issue Apr 21, 2018 · 28 comments
Labels
A-trait-system Area: Trait system C-bug Category: This is a bug. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@npmccallum
Copy link
Contributor

npmccallum commented Apr 21, 2018

Sorry for the code dump. This is the smallest code I could make to reproduce the problem.

use std::marker::PhantomData;
use std::convert::TryFrom;

trait Integer {}
impl Integer for u8 {}

trait Adapter<I: Integer>: TryFrom<I> + Into<I> {}

enum Choice {
    Foo,
    Bar,
    Baz
}

impl From<Choice> for u8 {
    fn from(c: Choice) -> u8 {
        match c {
            Choice::Foo => 1,
            Choice::Bar => 2,
            Choice::Baz => 3,
        }
    }
}

impl TryFrom<u8> for Choice {
    type Error = ();
    fn try_from(i: u8) -> Result<Choice, ()> {
        match i {
            1 => Ok(Choice::Foo),
            2 => Ok(Choice::Bar),
            3 => Ok(Choice::Baz),
            _ => Err(()),
        }
    }
}

impl Adapter<u8> for Choice {}

struct Pick<I: Integer, A: Adapter<I>> {
    phantom: PhantomData<A>,
    value: I,
}

impl<I: Integer, A: Adapter<I>> From<A> for Pick<I, A> {
    fn from(a: A) -> Pick<I, A> {
        Pick {
            phantom: PhantomData,
            value: a.into(),
        }
    }
}

impl<I: Integer, A: Adapter<I>> TryFrom<Pick<I, A>> for A {
    type Error = A::Error;

    fn try_from(p: Pick<I, A>) -> Result<A, Self::Error> {
        A::try_from(p.value)
    }
}

Attempting to compile this produces:

error[E0119]: conflicting implementations of trait `std::convert::TryFrom<Pick<_, _>>`:
  --> src/main.rs:53:1
   |
53 | impl<I: Integer, A: Adapter<I>> TryFrom<Pick<I, A>> for A {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: conflicting implementation in crate `core`:
           - impl<T, U> std::convert::TryFrom<U> for T
             where T: std::convert::From<U>;

error[E0210]: type parameter `A` must be used as the type parameter for some local type (e.g. `MyStruct<A>`)
  --> src/main.rs:53:1
   |
53 | impl<I: Integer, A: Adapter<I>> TryFrom<Pick<I, A>> for A {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type parameter `A` must be used as the type parameter for some local type
   |
   = note: only traits defined in the current crate can be implemented for a type parameter

I've spent several hours looking at this and I can't figure out why I'm getting these errors. The type parameter A is being used as the type parameter for a local type. Maybe the compiler can't tell because it is nested inside TryFrom<>?

But I'm also not sure why the first error occurs at all. The rule it conflicts with can basically be described as "types with infallible conversions implicitly implement fallible conversions." But in my case there is no infallible conversion. So I don't see where the conflict is arising from.

If there is really a bug and I'm not just overtired, this may impact #49305.

@XAMPPRocky XAMPPRocky added A-trait-system Area: Trait system T-lang Relevant to the language team, which will review and decide on the PR/issue. C-bug Category: This is a bug. labels Sep 25, 2018
@jonathanstrong
Copy link

jonathanstrong commented Jan 1, 2019

Ran into this issue as well. In my case:

use std::ffi::OsStr;
use std::path::PathBuf;
use std::io;

#[derive(Clone)]
pub struct DataSet {
    pub path: PathBuf,
    pub mountpoint: PathBuf,
}

pub fn get_mountpoint<P: AsRef<OsStr>>(path: P) -> Result<String, io::Error> {
    // ...
}

impl<P> TryFrom<P> for DataSet
    where P: AsRef<OsStr>
{
    type Error = io::Error;
    fn try_from(path: P) -> Result<Self, Self::Error> {
        let mountpoint: String = get_mountpoint(&path)?;
        let mountpoint = PathBuf::from(mountpoint);
        let path = path.as_ref().to_path_buf();
        Ok(Self { path, mountpoint })
    }
}

Error:

error[E0119]: conflicting implementations of trait `std::convert::TryFrom<_>` for type `<mod>::DataSet`:
  --> src/<file>.rs:80:1
   |
80 | / impl<P> TryFrom<P> for DataSet
81 | |     where P: AsRef<OsStr>
82 | | {
83 | |     type Error = io::Error;
...  |
89 | |     }
90 | | }
   | |_^
   |
   = note: conflicting implementation in crate `core`:
           - impl<T, U> std::convert::TryFrom<U> for T
             where T: std::convert::From<U>;

In this case, the DataSet struct is defined in the same mod, and it has no From impls at all.

@marrub--
Copy link

marrub-- commented Apr 12, 2019

I'm getting this issue as well on 1.34. It appears to happen any time you attempt to impl TryFrom with a bounded generic parameter (regardless of its position.) I'm not sure if this is a compiler bug or just me writing it wrong:

impl<'a, I> TryFrom<I> for CStringVec
   where I: Iterator<Item = &'a str>
{
   type Error = Error;

   /// Creates a new `CStringVec` from an iterator.
   #[inline]
   fn try_from(it: I) -> Result<Self, Self::Error>
   {
      // ...
   }
}
error[E0119]: conflicting implementations of trait `std::convert::TryFrom<_>` for type `durandal::ffi::CStringVec`:
  --> source/durandal/ffi.rs:42:1
   |
42 | / impl<'a, I> TryFrom<I> for CStringVec
43 | |    where I: Iterator<Item = &'a str>
44 | | {
45 | |    type Error = Error;
...  |
58 | |    }
59 | | }
   | |_^
   |
   = note: conflicting implementation in crate `core`:
           - impl<T, U> std::convert::TryFrom<U> for T
             where U: std::convert::Into<T>;

Edit: This is not a bug, although the error message potentially is one. The real issue is that you cannot implement a trait with a generic trait not from this crate.

@jean-airoldie
Copy link

jean-airoldie commented May 2, 2019

This is is due to this core blanket implementation. Since it is possible that a type implement both the core Into<T> bound as well as your custom trait bound, this create a potential implementation conflict that the compiler can't allow.

Not sure how to circumvent this without negative trait bounds.

edit: Updated outdated link

@CJKay
Copy link

CJKay commented Jul 3, 2019

This is really frustrating to work with. I have been unable to find a way to provide certain generic TryFrom impls like:

impl<T: ToString> TryFrom<T> for X { ... }

Collision.

impl<T: AsRef<str>> TryFrom<T> for X { ... }

Collision.

Argh!

@ytitov
Copy link

ytitov commented Jul 9, 2019

I would like to add that I would love a solution for this. I constantly use the From trait, but there are cases where TryFrom is more appropriate

@zesterer
Copy link
Contributor

This really is a very painful feature to use due to the aforementioned conflict with the blanket implementation. I gather that removing the blanket implementation is a breaking change, but I can't imagine that it is something that is heavily depended on. Removing this blanket implementation would definitely make these traits more useful.

oliverlee added a commit to oliverlee/morseqtt that referenced this issue Aug 31, 2019
TryFrom is implemented for '&char' instead of 'char' due to a conflict
with the blanket implementation.

For more details see:
rust-lang/rust#50133
https://old.reddit.com/r/rust/comments/c1yubv/why_this_code_does_not_compile/
oliverlee added a commit to oliverlee/morseqtt that referenced this issue Aug 31, 2019
TryFrom is implemented for '&char' instead of 'char' due to a conflict
with the blanket implementation.

For more details see:
rust-lang/rust#50133
https://old.reddit.com/r/rust/comments/c1yubv/why_this_code_does_not_compile/
oliverlee added a commit to oliverlee/morseqtt that referenced this issue Aug 31, 2019
TryFrom is implemented for '&char' instead of 'char' due to a conflict
with the blanket implementation.

For more details see:
rust-lang/rust#50133
https://old.reddit.com/r/rust/comments/c1yubv/why_this_code_does_not_compile/
@igozali
Copy link

igozali commented Nov 18, 2019

@jean-airoldie just wanted to clarify out of curiosity, the blanket implementation for TryInto/Into for TryFrom is this one, right?

https://github.com/rust-lang/rust/blob/master/src/libcore/convert.rs#L566-L586

Figured the Rust docs link might be outdated.

@jean-airoldie
Copy link

Yeah, I should have used a perma-link instead. I'll edit my comment.

@ckaran
Copy link

ckaran commented Feb 7, 2020

Does anyone know when/if this will be fixed? I've just stubbed my toe into it, and the only way that I can get around it is to bind to concrete types. The problem is that due to what I'm writing a newtype wrapper for, this will mean I have around 1300 essentially identical implementations for each and every newtype wrapper that I'm generating. I can write a code generator in python to speed to handle this, but that isn't ideal.

@AaronKutch
Copy link
Contributor

AaronKutch commented Feb 10, 2020

Is this related to the problem I am having refactoring the apint crate? I am refactoring some code involving the invariant management type BitWidth. There are many functions whose signature I want to change to TryInto<BitWidth, Error=CrateErrorType>. When I update the signatures, is enables one ergonomic feature where I can directly input a literal into the function (due to a TryFrom<usize> impl for BitWidth), but at the same time disables inputing a simple BitWidth into the function.

The reason for this is the blanket impl impl TryFrom<T> for T which has return type Error=Infallible which conflicts with the Error=CrateErrorType bound, thus causing a collision.

! can be coerced into anything, but can it be made to coerce in other ways?

Edit: I fixed the problem by adding

pub fn typical_function<W, E>(target_width: W) -> Result<(), MyError>
where
    W: TryInto<BitWidth, Error = E>,
    MyError: From<E>,

and a impl From<std::convert::Infallible> for MyError {, but the function signature seems roundabout to me

@sffc
Copy link

sffc commented Jun 20, 2020

This is a very annoying error.

Here is my workaround: if you have something like,

// E0119!
impl<T> TryFrom<T> for MyType { /* */ }

You can create a little wrapper struct, which I think should compile away as a zero-cost abstraction, to get around the compiler error.

pub struct InputWrapper<T>(T);
impl<T> TryFrom<InputWrapper<T>> for MyType { /* */ }

By removing the raw templatized parameter on the TryFrom, you can circumvent E0119. To use it, you just need to wrap the variable in InputWrapper:

MyType::try_from(InputWrapper(foo))

I hope that the problem can be somehow fixed upstream to avoid needing workarounds like this.

@wucke13
Copy link

wucke13 commented Jul 3, 2020

Would it be an option to insert a marker trait (empty trait with a distinct_and_long_name) that is a bound to the blanket implementation of the stdlib? That way people could opt in to this blanket if they need it without making it completely impossible to have generic TryFroms.

@ckaran
Copy link

ckaran commented Nov 5, 2020

Anyone know how this is going? I'm trying to clean up my code, and it isn't going well because of this bug.

@cmpute
Copy link

cmpute commented Jan 11, 2022

Does it make sense to let the compiler prefer a more strict implementation than looser one? For example, when two implementation of the same trait on a potentially conflict type, choose the more restricted one rather than preventing a new implementation.

Lets say we implement impl<U> TryFrom<U> for SomeType, since here we have a specific/specialized type for T in impl<T, U> TryFrom<U> for T, then the new implementation should be chosen over the blanket implementation. (sounds like the partial template specialization in C++, but I guess a simpler version which only override blanket implementation should be easier?)

@ckaran
Copy link

ckaran commented Jan 11, 2022

@cmpute As I understand it, you've just described the specialization feature that has been on nightly since forever. It exists, but (again, as I understand it), there are issues that have so far prevented it from landing on stable.

refi64 added a commit to refi64/gitlab-runner-rs that referenced this issue Mar 31, 2022
This does require the removal of the From implementation; it couldn't be
converted to TryFrom due to lack of generic specialization
(rust-lang/rust#50133).

Signed-off-by: Ryan Gonzalez <ryan.gonzalez@collabora.com>
refi64 added a commit to refi64/gitlab-runner-rs that referenced this issue Mar 31, 2022
This does require the removal of the From implementation; it couldn't be
converted to TryFrom due to lack of generic specialization
(rust-lang/rust#50133).

Signed-off-by: Ryan Gonzalez <ryan.gonzalez@collabora.com>
sjoerdsimons pushed a commit to collabora/gitlab-runner-rs that referenced this issue Mar 31, 2022
This does require the removal of the From implementation; it couldn't be
converted to TryFrom due to lack of generic specialization
(rust-lang/rust#50133).

Signed-off-by: Ryan Gonzalez <ryan.gonzalez@collabora.com>
@ghost
Copy link

ghost commented Jun 3, 2022

Why does this receive so little attention? This is a big usability problem affecting the whole trait system.

@ckaran
Copy link

ckaran commented Jun 3, 2022

@leontepe You can try talking about changing this starting in Rust 2024. It probably wouldn't be changed by then, but a deprecation warning could be issued for the blanket implementation that is the cause of this problem, and then it could be removed in Rust 2027. However, since it is a part of stable Rust, removing it or changing it could be very, very hard to do. I think that's the reason no-one has tried to do so yet; it's an annoying wart, but not something that breaks the safe/unsafe separation, or causes critical breakage, so no-one has put in the effort to make the change.

@jakob-lilliemarck
Copy link

I believe I also just issue in a multi-crate project:

// entities.rs

pub trait Tuple {
    type Tuple;

    fn to_tuple(&self) -> Self::Tuple;
}

impl Tuple for Script {
    type Tuple = (
        Option<i32>,
        String,
        String,
        Option<String>,
        bool,
        Option<i32>,
        Option<i32>,
    );

    fn to_tuple(&self) -> <Self as Tuple>::Tuple {
        (
            self.id.0.clone(),
            self.name.0.clone(),
            self.source.0.clone(),
            self.description.0.clone(),
            self.is_active,
            self.owner_id,
            self.output_id,
        )
    }
}

and then in another crate:

pub struct ScriptNew {
    pub name: String,
    pub source: String,
    pub description: Option<String>,
    pub is_active: bool,
    pub owner_id: Option<i32>,
    pub output_id: Option<i32>,
}

impl From<<Script as Tuple>::Tuple> for ScriptNew {
    fn from(script: <ScriptEntity as Tuple>::Tuple) -> Self {
        !unimplemented!()
    }
}

It would indeed be very useful to be able to do this - as it guarantees the implemented tuple-type on some core-library-entity is of the same type as implemented in a consuming application or library.

I can work around it by exposing the a pub type ScriptTuple from the first crate, but that opens up loophole for human error where the implemented type could differ from the exposed type.

pub type ScriptTuple = (
    Option<i32>,
    String,
    String,
    Option<String>,
    bool,
    Option<i32>,
    Option<i32>,
);

impl Tuple for Script {
    type Tuple = ScriptTuple;

    fn to_tuple(&self) -> <Self as Tuple>::Tuple {
        (
            self.id.0.clone(),
            self.name.0.clone(),
            self.source.0.clone(),
            self.description.0.clone(),
            self.is_active,
            self.owner_id,
            self.output_id,
        )
    }
}

@ms-ati
Copy link

ms-ati commented Feb 21, 2023

Can the language team be persuaded to prove their hypothesis that dropping the blanket implementation would break real, intentional code in the wild? It's fine if it does, but does it really?

@criloz
Copy link

criloz commented Mar 3, 2023

Just for this trait, make an exception, allow people to redefine impl TryFrom<Self> for Self>, and having custom errors. It just makes everything easier, not need to propagate this feature to other traits.

@ckaran
Copy link

ckaran commented Mar 7, 2023

@criloz I understand where you're coming from, but trust me, special casing something like TryFrom in this way will lead to a whole can of worms that nobody wants to deal with. I agree with @ms-ati, that it would be better to do a crater run and see how much code breaks because of the removal of the blanket implementation. Right now we're all just grasping at straws regarding what removing the blanket implementation would actually do.

@AAlvarez90
Copy link

For some reason I cannot have an implementation for TryFrom and From in the same file:

struct Person {
    first_name: String,
    last_name: String,
    age: usize,
}

impl TryFrom<&str> for Person {
    type Error = &'static str;
    fn try_from(value: &str) -> Result<Self, Self::Error> {

        let first_name;
        let last_name;
        let age: usize;

        let mut parts = value.split(',');

        if let Some(first) = parts.next() {
            first_name = first;
        } else {
            return Err("Missing name");
        }

        if let Some(last) = parts.next() {
            last_name = last;
        } else {
            return Err("Missing last name");
        }

        if let Some(ag) = parts.next() {
             match ag.parse::<usize>() {
                Ok(val) => age = val,
                Err(e) => {
                    println!("Couldn't parse it: {} ", e);
                    return Err("Age is missing");
                }
             }
        } else {
            return Err("Missing age");
        }

        Ok(Self {
            first_name: String::from(first_name),
            last_name: String::from(last_name),
            age
        })
    }
}

impl From<&str> for Person {
    fn from(value: &str) -> Self {

        let first_name;
        let last_name;
        let age: usize;

        let mut parts = value.split(',');

        if let Some(first) = parts.next() {
            first_name = first;
        } else {
            first_name = "";
        }

        if let Some(last) = parts.next() {
            last_name = last;
        } else {
            last_name = "";
        }

        if let Some(ag) = parts.next() {
            age = ag.parse::<usize>().unwrap();
        } else {
            age = 0;
        }

        Self {
            first_name: String::from(first_name),
            last_name: String::from(last_name),
            age
        }
    }
}

fn main() {
    let p = Person::from("John,Doe,31");
    println!("The person is: {:#?}", p);

    // If you inplement From, into will be implemented automatically since internally it called: From.
    let p1: Person = "Jane,Doe,30".into();
    println!("HERE INTO: {:#?}", p1);

    let result: Result<Person, &str> = "John,D,haha".try_into();
    assert_eq!(result, Err("Age is missing"));

}

I am new to Rust but looking at some other comments, it seems that something kinda lame is in the way and it's preventing the above from working?

@OST-Gh
Copy link

OST-Gh commented Sep 12, 2023

@AAlvarez90, the TryFrom implementation would better be something for a FromStr implementation, though i think FromStr is just a special case of TryFrom<T> where T is &str.

@tmandry
Copy link
Member

tmandry commented Apr 19, 2024

The compiler now emits a different error for the original code. I think the old error was misleading, and the blanket From impl had nothing to do with why it didn't compile. The new error correctly points to a violation of the orphan rules:

error[E0210]: type parameter `A` must be covered by another type when it appears before the first local type (`Pick<I, A>`)
  --> src/lib.rs:53:18
   |
53 | impl<I: Integer, A: Adapter<I>> TryFrom<Pick<I, A>> for A {
   |                  ^ type parameter `A` must be covered by another type when it appears before the first local type (`Pick<I, A>`)
   |
   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last

For more information about this error, try `rustc --explain E0210`.

For an explanation of why these rules exist and how they came to be, see https://smallcultfollowing.com/babysteps/blog/2015/01/14/little-orphan-impls/.

@lalala-233
Copy link

lalala-233 commented Jul 26, 2024

I hope this will be dealt with soon.

Now I have to write like this.

struct A {}
trait B {}
struct Wrapper<T>(T);

impl<T: B> TryFrom<Wrapper<T>> for A {
    type Error = ();
    fn try_from(value: Wrapper<T>) -> Result<Self, Self::Error> {
        let value = value.0;
        todo!()
    }
}

Now I find I have to use pub struct Wrapper<T>(pub T); and use crate::Wrapper when I use TryFrom.

Another solution is use a Builder type or use fn try_from<T> to pretend you have a TryFrom trait...

@fmease fmease added A-trait-system Area: Trait system and removed A-trait-system Area: Trait system labels Dec 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-trait-system Area: Trait system C-bug Category: This is a bug. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests