E0207 despite parameters were used in the Self

Hello. I need some help with this. I read the description of E0207, but it's not clear to me how to apply it to my case and how to make the code work. Despite Arg1 and Res being used in self type it's not enough seems.

fn main()
{

  let _f0 = ||
  {
    println!( "hello" );
  };

  let _f1 = | a : i32 |
  {
    println!( "{}", a );
  };

  let _f2 = | a : i32, b : u32 |
  {
    println!( "{} {}", a, b );
  };

  assert_eq!( _f0.is(), true );
  assert_eq!( f0.is(), true );
  assert_eq!( f0f.is(), true );

  assert_eq!( _f1.is(), true );
  assert_eq!( f1.is(), true );
  assert_eq!( f1f.is(), true );

}

//

// fn is1< Res, F : Fn() -> Res >( _ : F ) -> bool { true }

fn f0() {}
fn f0f() -> bool { false }
fn f1( _ : i32 ) {}
fn f1f( _ : i32 ) -> bool { false }

//

trait IntoRoutine
{
  fn is( &self ) -> bool;
}

//

impl< Res, Ro > IntoRoutine
for Ro
where Ro : Fn() -> Res
{
  fn is( &self ) -> bool
  {
    true
  }
}

//

impl< Arg1, Res, Ro > IntoRoutine
for Ro
where
  Ro : Fn( Arg1 ) -> Res,
{
  fn is( &self ) -> bool
  {
    true
  }
}

Output:

error[E0207]: the type parameter `Arg1` is not constrained by the impl trait, self type, or predicates
  --> src/main.rs:60:7
   |
60 | impl< Arg1, Res, Ro > IntoRoutine
   |       ^^^^ unconstrained type parameter

error[E0207]: the type parameter `Res` is not constrained by the impl trait, self type, or predicates
  --> src/main.rs:60:13
   |
60 | impl< Arg1, Res, Ro > IntoRoutine
   |             ^^^ unconstrained type parameter

For more information about this error, try `rustc --explain E0207`.

Playground.

Any hint?

The point of this error is that, in this context, an "unconstrained" type parameter is one that does not participate in the construction of the type. In your case, even though you technically write down Arg1 and Res, those types don't cause the trait or Ro, the type you are implementing the trait for, to change, i.e. neither the identity of Ro nor the identity of IntoRoutine depends on either Arg1 or Res.

If you resolve the universal quantifiers, your code essentially means this in plain English: "implement IntoRoutine for all types Ro as long as those types implement Fn with argument type Arg1 and return type Res".

Since a particular type might implement Fn with many different combinations of Arg1 and Res, this could cause the same trait (IntoRoutine) to be implemented multiple times for the same type, which is not allowed.


What you probably meant to do is declare IntoRoutine with a type parameter and an associated type. That does indeed make more sense: a particular object may implement different IntoRoutine<Arg> traits, because it may be able to accept more than one kind of argument. And the type of the argument probably determines the return type, so generally that would be an associated type. Playground.


As an aside, please look at some real-world Rust code and/or examples in The Book or the std documentation in order to learn idiomatic code formatting.

3 Likes

Thanks for the answer, but I don't understand how to apply your advice to the original problem :frowning: . My goal to have an answer to the question "is that a function implementing trait Fn".

Original problem.
Does any kind of a solution to the problem exist in Rust?

You have to add the Arg to the trait as a generic type parameter, and actually use Res:

trait IntoRoutine<Arg> {
    type Res;
    fn is(&self) -> bool;
}

impl<Arg, Res, Ro> IntoRoutine<Arg> for Ro
where
    Ro: Fn(Arg) -> Res,
{
    type Res = Res;
}

Edit: wait, I already gave the exact same sample code in my first answer. What's wrong with that?

1 Like

Your solution changed

 - fn is(&self) -> bool
 + fn is(&self, arg: Arg1) -> Self::Ret

Thank you. Trying.

type does not help
Probably I do something wrong.

You still did not add Add as a type parameter to the trait, and you are still using Self::Res1 in the where clause constraining the Fn itself, instead of the Res type parameter. This compiles. Again, this is the exact same code I gave before.

1 Like

Now I see. Arg should be passed as parameter to the trait. And Type is not necessary.

Trying to extrapolate that on 2-arguments length function I encountered another problem. Maybe you have a suggestion how to solve that?

Those two impls overlap. A type parameter means universal quantification (<T> can be read as "for all types T"). Thus, if you substitute Arg2 = () into the second impl, you have the <Arg1, ()> impl twice. This doesn't make sense. You should probably implement a single parameter only, and use tuples when necessary.

1 Like

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.