Unconstrained type parameter


#1

I have the following definitions:

trait Constructed {
    type Data;
}

struct A(i32);

impl Constructed for A {
    type Data = i32;
}

This works fine if I implement the following fn:

fn fn_with_a<A: Constructed, F: Fn(A::Data) -> A>(f: &F) {
    unimplemented!()
}

If I try to do basically the same thing but with a trait, I get an unconstrained type parameter error:

trait Dummy {
    fn trait_fn_with_a(&self);
}

impl<A: Constructed, F: Fn(A::Data) -> A> Dummy for F {
    fn trait_fn_with_a(self: &F) {
        fn_with_a(self)
    }
}

Why doesn’t this work?

https://is.gd/vFt24o


#2

This is because you’re not using A anywhere in your impl Dummy. Try adding a generic parameter <A> to Dummy.


#3

So it turns out this works and doesn’t error for some reason:

fn test<T>() {}

#4

And this works too:


fn fn_with_a<A: Constructed, F: Fn(i32) -> A>(f: &F) {
    unimplemented!()
}

trait Dummy {
    fn trait_fn_with_a(&self);
}

impl<A: Constructed, F: Fn(i32) -> A> Dummy for F {
    fn trait_fn_with_a(&self) {
        fn_with_a(self)
    }
}

I think the real problem is not an unconstrained type but a cyclic constraint between F and F::Output.


#5

A function can have multiple Fn impls: (playpen)

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

// I'm keeping this example to FnOnce because it's easiest to implement.
// (doing Fn would require a bunch more boilerplate)
struct Funk;

impl FnOnce<((),)> for Funk {
    type Output = ();

    extern "rust-call"
    fn call_once(self, arg: ((),)) -> () { arg.0 }
}

impl FnOnce<(u8,)> for Funk {
    type Output = bool;

    extern "rust-call"
    fn call_once(self, arg: (u8,)) -> bool { arg.0 % 2 == 0 }
}

trait Constructed { type Data; }
impl Constructed for ()   { type Data = (); }
impl Constructed for bool { type Data = u8; }

fn fn_with_a<A: Constructed, F: FnOnce(A::Data) -> A>(f: F) -> usize {
    ::std::mem::size_of::<A>()
}

In this case, fn_with_a(Funk) has two possible implementations; but it is still possible to disambiguate between them:

fn main() {
    // error[E0284]: type annotations required: cannot resolve `<_ as Constructed>::Data == _`
    println!("{:?}", fn_with_a(Funk));

    // okay
    println!("{:?}", fn_with_a::<(),_>(Funk));   // prints 0
    println!("{:?}", fn_with_a::<bool,_>(Funk)); // prints 1
}

In your trait example, the parameter is on the impl. Those parameters however play no role in distinguishing bewteen trait impls; the full path of a trait method is

<MyType<A,B> as MyTrait<F,G>>::my_method::<X,Y>()

End result: Your original impl, if it were allowed, would create two conflicting implementations for <Funk as Dummy>.