Rust: Invalid collision with TryFrom implementation?

Created on 21 Apr 2018  路  16Comments  路  Source: rust-lang/rust

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.

A-traits C-bug T-lang

Most helpful comment

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!

All 16 comments

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.

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.

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

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!

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

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.

@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.

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

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.

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

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.

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.

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

@ckaran Technically, this is not really a bug, more an ugly wart in the API. Unfortunately I do not expect this to be addressed any time soon.
Basically: To make anything which is Into<U> be usable in an interface which takes an TryInto<U>, a blanket implementation has been made. This blanket implementation now blocks of other Blanket implementations for TryInto<U> no matter what U is, as there can not be two competing implementations of one trait (TryInto<U>).

Will specialization make the wart go away? Serious question, I know of specialization, but don't know how it works, or if it would fix this issue.

@ckaran I believe so, yes.

Was this page helpful?
0 / 5 - 0 ratings