Conflicting implementation when implementing traits for Fn

I am trying to implement a special trait for all functions that match certain criteria. Unfortunately, the compiler thinks that my trait implementations are conflicting. Here is a link to a minimal example which causes this error. Aren't Fn() -> u32 and Fn(u32) -> u32 supposed to be different types?

I found this project which does something similar, and I have no clue why that version works when mine does not.

2 Likes

Technically Fn(*) -> *, FnMut(*) -> * and FnOnce(*) -> * aren't types at all. They're traits (which aren't types in Rust), and each can refer to a closure including its environment (i.e. its free variables), or it can just refer to a fn item.
This becomes apparent when you want to write a function that takes a closure as an argument e.g.:

fn foo<F>(closure: F) 
where 
    // you can't construct a trait bound using types. 
    // Thus the Fn part is a trait:
    F: Fn(usize) -> usize 
{
    let _closure_output = closure(0);
}

Now that all that's cleared up, I should also mention that you can't implement a trait for another trait.

But what's really tripping up your example is the fact that you're trying to write 2 blanket impls for your Foo trait. I think rustc disallows this because those 2 impls could overlap, at least in principle.

2 Likes

As far as I’m aware, no closure or fn item that you can currently write in stable rust will create an overlapping implementation for Fn() -> u32 and Fn(u32) -> u32. It is however possible in nightly, e.g.:

#![feature(fn_traits)]
#![feature(unboxed_closures)]

#[allow(nonstandard_style)]
struct overloaded_fn;

impl FnOnce<(u32,)> for overloaded_fn {
    type Output = u32;
    extern "rust-call" fn call_once(self, args: (u32,)) -> u32 {
        self.call(args)
    }
}
impl FnMut<(u32,)> for overloaded_fn {
    extern "rust-call" fn call_mut(&mut self, args: (u32,)) -> u32 {
        self.call(args)
    }
}
impl Fn<(u32,)> for overloaded_fn {
    extern "rust-call" fn call(&self, (x,): (u32,)) -> u32 {
        2*x
    }
}

impl FnOnce<()> for overloaded_fn {
    type Output = u32;
    extern "rust-call" fn call_once(self, args: ()) -> u32 {
        self.call(args)
    }
}
impl FnMut<()> for overloaded_fn {
    extern "rust-call" fn call_mut(&mut self, args: ()) -> u32 {
        self.call(args)
    }
}
impl Fn<()> for overloaded_fn {
    extern "rust-call" fn call(&self, (): ()) -> u32 {
        42
    }
}

fn main() {
    println!("{}", overloaded_fn(123));
    println!("{}", overloaded_fn());
}

(playground)

The reason why the implementations of IntoSystem that you linked aren’t overlapping is that they made sure that the implementations of IntoSystem<Params, SystemType> differ in their Params argument, e.g. using different length tuple types, etc.

Since type inference will actually deduce the parameter to the trait in case there is only a single matching implementation, you can probably use the same “trick” for your example

trait Foo<T> {
    fn bar(&self, x: u32) -> u32;
}

impl<F> Foo<(u32,)> for F
    where F: Fn(u32) -> u32
{
    fn bar(&self, x: u32) -> u32 {
        (self)(x)
    }
}

impl<F> Foo<()> for F 
    where F: Fn() -> u32 
{
    fn bar(&self, x: u32) -> u32 {
        (self)() + x
    }        
}

fn main() {
    fn get_5() -> u32 {
        5
    }
    
    let square = |x: u32| x * x;
    
    println!("{}", get_5.bar(10));
    println!("{}", square.bar(2));
}

(playground)

5 Likes

Thanks you so much!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.