Rust: type parameter not constrained when using closure bounds

Created on 2 May 2015  Â·  18Comments  Â·  Source: rust-lang/rust

This works fine:

trait Foo {}
impl<F, A> Foo for F where F: Fn() -> A {}

But this:

trait Foo {}
impl<F, A> Foo for F where F: Fn(A) {}

produces a compile error:

<anon>:2:9: 2:10 error: the type parameter `A` is not constrained by the impl trait, self type, or predicates [E0207]
<anon>:2 impl<F, A> Foo for F where F: Fn(A) {}

I'm not sure if this is intended behavior or not, but I definitely don't understand why the former is accepted and the latter is not.

Version:

[andrew@Liger quickcheck] rustc --version
rustc 1.1.0-nightly (c4b23aec4 2015-04-29) (built 2015-04-29)
A-diagnostics C-enhancement D-confusing D-newcomer-roadblock F-unboxed_closures T-compiler

Most helpful comment

Is the error message misleading, or am I missing a nuance?

trait Foo {}

impl<F, A> Foo for F
    where F: Fn(A),
          A: Foo,
{}

Has the error

the type parameter A is not constrained by the impl trait, self type, or predicates

But, I _do_ have a constraint on A in the predicates — A: Foo. Which part of "constraint" or "predicate" am I misreading?

All 18 comments

Another data point:

trait Foo {}
impl<F, A, T> Foo for F where F: Fn(A) -> T {}

produces

<anon>:2:9: 2:10 error: the type parameter `A` is not constrained by the impl trait, self type, or predicates [E0207]
<anon>:2 impl<F, A, T> Foo for F where F: Fn(A) -> T {}
                 ^
<anon>:2:12: 2:13 error: the type parameter `T` is not constrained by the impl trait, self type, or predicates [E0207]
<anon>:2 impl<F, A, T> Foo for F where F: Fn(A) -> T {}

Which says _both_ types are not constrained. Weird.

a slightly reduced test case demonstrating that it's not the Fn family of traits in particular:

https://play.rust-lang.org/?gist=93a594257a2c08bd33c3&version=nightly

trait MyFn<In> {
    type Out;
}

trait Foo {}

impl<F, A> Foo for F where F: MyFn<A, Out=()> {}

fn main() {}
<anon>:7:9: 7:10 error: the type parameter `A` is not constrained by the impl trait, self type, or predicates [E0207]
<anon>:7 impl<F, A> Foo for F where F: MyFn<A, Out=()> {}
                 ^
<anon>:7:9: 7:10 help: see the detailed explanation for E0207
error: aborting due to previous error
playpen: application terminated with error code 101

This is completely intentional - see rust-lang/rfcs#447. You can (given #![feature(unboxed_closures)]) manually implement Fn with multiple distinct sets of input parameters:

#![feature(unboxed_closures,core)]
use std::mem;

trait Foo {
    fn foo(&self) -> usize;
}
impl<F, A> Foo for F where F: FnOnce(A) {
    fn foo(&self) -> usize { mem::size_of::<A>() }
}

struct S;
impl FnOnce<(u32,)> for S {
    type Output = ();
    extern "rust-call" fn call_once(self, _args: (u32,)) {}
}
impl FnOnce<(u8,)> for S {
    type Output = ();
    extern "rust-call" fn call_once(self, _args: (u8,)) {}
}
fn main() {
    println!("{}", <S as Foo>::foo(&S)); // which impl is used?
}

The "correct" fix is probably to add a type parameter to foo.

There seems to be a related issue in which the checker is overly conservative:

trait Foo {}

struct Bar<T> { _b: T }

impl<E, T: Unsize<E>> Foo for Bar<T> {}

does not compile, even though E can be determined uniquely.

@alevy

Unsize is a trait with a type parameter, not an associated type. I don't remember why @eddyb did it that way, but this is how it is. [u8; 1] implements both Unsize<[u8]> and Unsize<fmt::Debug>.

Each type can implement many traits and T: Trait implies T: Unsize<Trait> (if the trait is object-safe).

But if you know T, you also know E. Bar<T> means that you know T, so E is never ambiguous in the impl.

@alevy That's just plain wrong, there are many possible substitutions for E, @arielb1 even gave an example....
Unless you meant to write Unsize<[E]>.

@eddyb I see. you're right. I actually did mean Unsize<[E]>, but I don't understand why that makes a difference. Will try to ask on IRC instead of spamming this issue though.

Is the error message misleading, or am I missing a nuance?

trait Foo {}

impl<F, A> Foo for F
    where F: Fn(A),
          A: Foo,
{}

Has the error

the type parameter A is not constrained by the impl trait, self type, or predicates

But, I _do_ have a constraint on A in the predicates — A: Foo. Which part of "constraint" or "predicate" am I misreading?

@shepmaster it's definitely misleading - effectively, what it's trying to tell you is that it cannot get A back from either the implemented trait (Foo) or the type implemented on (F).
F: Fn(A) is not enough to extract A from F because one F type can have multiple Fn impls with various arguments (although this wouldn't happen with closures).

I'm hitting this issue too. Is it true for all functions and closures that A is uniquely specified by the type of F? It would be nice to have a trait that enforces that, since most of the types that implement Fn/FnMut/FnOnce have a specified input and return type.

trait ActuallyAFn {
  type Param;
  type Return;
  fn call(&self, param: Self::Param) -> Return;
}

I also meet this issue while writing the following code:

use std::ops::Add;

pub struct Input<'a, I: 'a> {
  pub data: &'a [I],
  pub position: usize,
}

impl<'a, I: 'a> Input<'a, I> {
  pub fn new(input: &'a [I]) -> Input<I> {
    Input {
      data: input,
      position: 0,
    }
  }

  pub fn current(&self) -> Option<I>
    where I: Copy + Clone + 'static
  {
    if self.position < self.data.len() {
      Some(self.data[self.position])
    } else {
      None
    }
  }

  pub fn advance(&mut self) {
    self.position += 1;
  }
}

#[derive(Debug, PartialEq)]
pub enum Error {
  Incomplete,
  Mismatch { message: String, position: usize },
}

pub type Result<O> = ::std::result::Result<O, Error>;

pub struct Parser<I, O> {
  method: Box<Fn(&mut Input<I>) -> Result<O>>,
}

impl<I, O> Parser<I, O> {
  pub fn new<P>(parse: P) -> Parser<I, O>
    where P: Fn(&mut Input<I>) -> Result<O> + 'static
  {
    Parser { method: Box::new(parse) }
  }

  pub fn parse(&self, input: &mut Input<I>) -> Result<O> {
    (self.method)(input)
  }
}

impl<I, O1, O2> Add for Parser<I, O1> {
  type Output = Parser<I, (O1, O2)>;

  fn add(self, other: Parser<I, O2>) -> Self::Output
    where I: 'static,
          O1: 'static,
          O2: 'static
  {
    Parser::new(move |input: &mut Input<I>| {
      self.parse(input).and_then(|out1| other.parse(input).map(|out2| (out1, out2)))
    })
  }
}

The error message is:

error[E0207]: the type parameter `O2` is not constrained by the impl trait, self type, or predicates
  --> src/parser.rs:81:13
   |
81 | impl<I, O1, O2> Add for Parser<I, O1> {
   |             ^^ unconstrained type parameter

@J-F-Liu Completely intentional, associated types are supposed to be determined from Self and trait parameters alone but yours is extra generic.

EDIT: on a third read I noticed the O2 in the add argument. If the add signature correct then you forgot to specify the optional RHS type, i.e. the impl should not be for Add but Add<Parser<I, O2>>.

@eddby ah, thanks, the following code works:

impl<I, O, U> Add<Parser<I, U>> for Parser<I, O> {
    type Output = Parser<I, (O, U)>;

    fn add(self, other: Parser<I, U>) -> Self::Output
        where I: 'static,
              O: 'static,
              U: 'static
    {
        Parser::new(move |input: &mut Input<I>| {
            self.parse(input).and_then(|out1| other.parse(input).map(|out2| (out1, out2)))
        })
    }
}

The above code is critical for my parser combinator library.

While writing pom 2.0 meet this error again:

pub enum Error {
  Incomplete,
  Mismatch { message: String, position: usize },
  Conversion { message: String, position: usize },
  Custom { message: String, position: usize, inner: Option<Box<Error>> },
}
pub type Result<O> = ::std::result::Result<O, Error>;
pub trait Parser<'a, I, O> {
  fn parse(&self, input: &'a [I], start: usize) -> Result<(O, usize)>;
}

pub struct Left<P1, P2>(P1, P2);
impl<'a, I, O1, O2, P1: Parser<'a, I, O1>, P2: Parser<'a, I, O2>> Parser<'a, I, O1> for Left<P1, P2> {
  fn parse(&self, input: &'a [I], start: usize) -> Result<(O1, usize)> {
    self.0.parse(input, start).and_then(|(out1, pos1)|
      self.1.parse(input, pos1).map(|(_, pos2)|
        (out1, pos2)
      )
    )
  }
}
13 | impl<'a, I, O1, O2, P1: Parser<'a, I, O1>, P2: Parser<'a, I, O2>> Parser<'a, I, O1> for Left<P1, P2> {
   |                 ^^ unconstrained type parameter



md5-62d91da90820b76217dcc558a2501290



429 | impl Sub> for Combinator


| ^ unconstrained type parameter
```

Interestingly, this works:

use std::marker::PhantomData;

trait Foo {}

struct Function<F, I, O> {
    in_p: PhantomData<I>,
    out_p: PhantomData<O>,
    function: F
}

impl<F, I, O> Foo for Function<F, I, O>
    where F: Fn(I) -> O
{}

But this doesn't:

use std::marker::PhantomData;

trait Foo {}

struct Function<F> {
    function: F
}

impl<F, I, O> Foo for Function<F>
    where F: Fn(I) -> O
{}

@rkap That is a direct consequence of our rules. Do note that you don't need O in a PhantomData because it's fully determined by F and I.

Was this page helpful?
0 / 5 - 0 ratings