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

Implementation of Trait for different type of Fn doesn't work #60074

Open
richerarc opened this issue Apr 18, 2019 · 11 comments
Open

Implementation of Trait for different type of Fn doesn't work #60074

richerarc opened this issue Apr 18, 2019 · 11 comments
Labels
A-closures Area: closures (`|args| { .. }`) A-traits Area: Trait system T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@richerarc
Copy link

Implementing a Trait for a type T (where T is a function with a given a signature) and then trying to implement the same trait for another type T (where T is a function with a different signature) doesn't compile.

I tried this code:

trait MyTrait {}

impl<F> MyTrait for F where F: Fn(u32) -> u32 {}

impl<F> MyTrait for F where F: Fn(u32, u32) -> u32 {}

When compiling this code I get :

error[E0119]: conflicting implementations of trait `MyTrait`:
 --> src/main.rs:5:1
  |
3 | impl<F> MyTrait for F where F: Fn(u32) -> u32 {}
  | ------------------------------------------------------ first implementation here
4 | 
5 | impl<F> MyTrait for F where F: Fn(u32, u32) -> u32 {}
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation

What I would expect is that T here represent in the first implementation either a fn(u32) -> u32 or a closure the implement Fn(u32) -> u32, then in the second implementation, T would be either a fn(u32, u32) -> u32 or a closure the implement Fn(u32, u32) -> u32.

Meta

rustc 1.34.0 (91856ed 2019-04-10)
binary: rustc
commit-hash: 91856ed
commit-date: 2019-04-10
host: x86_64-apple-darwin
release: 1.34.0
LLVM version: 8.0

@jonas-schievink
Copy link
Contributor

I'm pretty sure this is a known issue, but can't find an issue to reference right now

@jonas-schievink jonas-schievink added A-traits Area: Trait system T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Apr 18, 2019
@Nemo157
Copy link
Member

Nemo157 commented Apr 18, 2019

This is an issue because a closure could implement both Fn traits (even though they never will), e.g. using some nightly features you can manually implement the traits for a custom type:

impl FnOnce<(u32,)> for Foo {
    type Output = u32;
    extern "rust-call" fn call_once(self, args: (u32,)) -> Self::Output {
        args.0
    }
}

impl FnOnce<(u32, u32)> for Foo {
    type Output = u32;
    extern "rust-call" fn call_once(self, args: (u32, u32)) -> Self::Output {
        args.0
    }
}

There might be some way to make this work in the future with a new language feature, there's probably an issue about it open somewhere.

@Centril
Copy link
Contributor

Centril commented Apr 18, 2019

There might be some way to make this work in the future with a new language feature, there's probably an issue about it open somewhere.

For example through variadic closures.

@Centril Centril added T-lang Relevant to the language team, which will review and decide on the PR/issue. A-closures Area: closures (`|args| { .. }`) and removed T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Apr 18, 2019
@Nemo157
Copy link
Member

Nemo157 commented Apr 18, 2019

@Centril I was more thinking of something like forcing Fn traits to be mutually exclusive (maybe could even be accomplished by something as simple as moving Args to be an associated type), or some other way of propagating the fact that it's impossible to have an actual closure implement multiple versions of these traits (other than having generic implementations for HRTB etc.).

Alternatively something related to specialization so that both implementations can be provided and overlap could be dealt with.

But maybe variadic closures could do it as well.

@Centril
Copy link
Contributor

Centril commented Apr 18, 2019

@Nemo157 Sure; I was mostly thinking in terms of what can be done without a stable ability to implement these Fn traits directly... :)

@djugei
Copy link
Contributor

djugei commented Jul 1, 2019

Is there some workaround available for this issue? The best i can come up with right now is to wrap the functions in newtypes, which is slightly uncomfortable.

@aWeinzierl
Copy link

I'm pretty sure this is a known issue, but can't find an issue to reference right now

#20770

@dzmitry-lahoda
Copy link

@djugei was you able to wrap? i tried to wrap with into and from impls, but it said to me that somebody already impls these.

@ibraheemdev
Copy link
Member

ibraheemdev commented Dec 19, 2020

Why is this considered conflicting implementations?

trait Function {
    fn call(&self) -> i32;
}

impl<F: Fn() -> Result<i32, bool>> Function for F {
    fn call(&self) -> i32 {
        self().unwrap()
    }
}

impl<F: Fn() -> i32> Function for F {
    fn call(&self) -> i32 {
        self()
    }
}

// error[E0119]: conflicting implementations of trait `Function`

@Nemo157 mentioned that the original example is an is an issue because a closure could implement both Fn traits. However, in the above example, that would not be possible, because Fn is generic over Args, making it impossible to implement Fn twice with the same arguments, even if Output is different:

struct Foo;

impl FnOnce<()> for Foo {
    type Output = i32;
    extern "rust-call" fn call_once(self, args: ()) -> Self::Output {
        1
    }
}

impl FnOnce<()> for Foo {
    type Output = Result<i32, bool>;
    extern "rust-call" fn call_once(self, args: ()) -> Self::Output {
        Ok(1)
    }
}

// error[E0119]: conflicting implementations of trait `std::ops::FnOnce<()>` for type `Foo`

@geo-ant
Copy link

geo-ant commented Feb 15, 2021

I saw that actix web has a solution for implementing a common interface across different Fn types. I was interested in doing a similiar thing for a project of mine, so I took their solution and documented the crafty trick they used. If anyone's interested.

The basic idea is to make the base trait generic and specialize different variants of the trait depending on the argument list of the Fn type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-closures Area: closures (`|args| { .. }`) A-traits Area: Trait system 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

10 participants