Implementing foreign traits parameterized with custom types for foreign types

I was under impression (and multidispatch RFC supports this) that it should be possible to implement operation traits for built-in types provided that trait type parameters are custom types, e.g. something like this:

impl Add<BigFloat> for f64 {
    type Output = BigFloat;
    ...
}

However, such code does not compile:

/home/dpx-infinity/dev/lang/rust/projects/mpfr.rs/src/lib.rs:312:1: 319:2 error: the type `f64` does not reference any types defined in this crate; only traits defined in the current crate can be implemented for arbitrary types [E0117]
/home/dpx-infinity/dev/lang/rust/projects/mpfr.rs/src/lib.rs:312 impl Add<BigFloat> for f64 {
/home/dpx-infinity/dev/lang/rust/projects/mpfr.rs/src/lib.rs:313     type Output = BigFloat;
/home/dpx-infinity/dev/lang/rust/projects/mpfr.rs/src/lib.rs:314 
/home/dpx-infinity/dev/lang/rust/projects/mpfr.rs/src/lib.rs:315     #[inline]
/home/dpx-infinity/dev/lang/rust/projects/mpfr.rs/src/lib.rs:316     fn add(self, rhs: BigFloat) -> BigFloat {
/home/dpx-infinity/dev/lang/rust/projects/mpfr.rs/src/lib.rs:317         rhs + self

I certainly remember that I was told that if one of trait parameters is "local" type, then the whole trait is considered "local", so it can be implemented for any type, not only "local" ones. Apparently this is not the case.

Is this a bug or expected behavior?

Update An issue

12 comments · 75% upvoted · other discussions

comments

dbaupp

It turns out that the multidispatch RFC had a bug, in that it allowed sets of implementations that were incoherent (that is, the compiler had no way to know which impl to chose unambiguously for certain sets of types). It's really unfortunate, but (correct) compilers are hard.

See #19470 for more details. Maybe the rules can be tweaked/relaxed slightly in future.

dpx-infinity [OP]

That's bad :( Overloading operators on both sides is really important, IMO.

burntsushi

I think this also prevents generic conversion to Box<error::Error> too? I had to remove impls like this from my code:

impl error::FromError<MyErrorType> for Box<error::Error>
japaric

Yes, the pattern:

impl RemoteTrait<LocalType> for RemoteType {}

is being rejected now. If it's blocking any of your libraries, please report it in this issue

dpx-infinity [OP]

Ok, I went on and submitted an issue. Hopefully this indeed is a (fixable) bug.

[deleted]

Report it as a bug -- I thought this was an expected feature. Like you said, one of the input types determining the impl is local.

What's your rustc version though? The checking rules have been updated multiple times recently.

dpx-infinity [OP]

I'm using the latest available nightly:

rustc 1.0.0-nightly (ea6f65c5f 2015-01-06 19:47:08 +0000)

Unfortunately, no newer nightlies are available because buildbot fails for some reason again.

japaric

And I can confirm this doesn't work with master (rustc 1.0.0-dev (5364c4853 2015-01-08 09:04:49 +0000)) either.

riccieri

From the new rules, it sounds like the design for the operators should be changed to contain both the LHS and RHS on the Self type if we want to allow overloading on both sides.

Could we do something like this?

on libcore:

trait Add {
    type Output;

    /* ... */
}

on user libraries:

impl Add for (f64, BigFloat) {
    type Output = BigFloat;

    /* ... */
}

impl Add for (BigFloat, f64) {
    type Output = BigFloat;

    /* ... */
}

(is there a way to express that a trait can be implemented only by pairs?)

UsaTewi

I hope there will be an attribute to allow overlapping instances. I've found that Rust keeps implementing Haskell extensions :3

sellibitze

Weird. I tried something like this before and it kind of worked. Not fully, but halfway. IIRC the problem was that method lookup didn't find the trait impl but I was able to call it via UFCS: Add::add(3.0, my_own_type). And now rustc is apparently even rejecting the impl block. :-(

I hope this gets fixed eventually. These “strict coherence” rules are kind of a PITA.

The_Doculope

It definitely worked for me about a week back. The operators didn't, but that was an outstanding issue I believe. Assigning the f32 to a x then using x.add(y) did work. This is definitely a new issue.