-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
type parameter not constrained when using closure bounds #25041
Comments
Another data point: trait Foo {}
impl<F, A, T> Foo for F where F: Fn(A) -> T {} produces
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
|
This is completely intentional - see rust-lang/rfcs#447. You can (given #![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 |
There seems to be a related issue in which the checker is overly conservative:
does not compile, even though |
Each type can implement many traits and |
But if you know |
@eddyb I see. you're right. I actually did mean |
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
But, I do have a constraint on |
@shepmaster it's definitely misleading - effectively, what it's trying to tell you is that it cannot get |
I'm hitting this issue too. Is it true for all functions and closures that
|
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:
|
@J-F-Liu Completely intentional, associated types are supposed to be determined from EDIT: on a third read I noticed the |
@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)
)
)
}
}
Can not be solved this time. I tried to add PhantomData, but transfer the error to the following code: impl<P, Q, O> Sub<Combinator<Q>> for Combinator<P>
{
type Output = Combinator<Left<P,Q,O>>;
fn sub(self, other: Combinator<Q>) -> Self::Output {
Combinator(Left(self.0, other.0, PhantomData))
}
}
|
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 |
I tried to implement a pipeline with steps. Steps implement the Processable trait. I failed to implement that trait directly for I suppose this is not possible, right now, correct? pub struct PipelineContext {
}
impl PipelineContext {
pub fn teardown(self) -> Box<dyn PipelineProcessor> {
self.processor
}
}
pub trait Processable {
type Input;
type Output;
fn process(&self, input: Self::Input, context: &mut PipelineContext) -> Self::Output;
}
pub struct PipelineStep<P, N>
where
P: Processable,
N: Processable<Input = P::Output>,
{
process: P,
next: N,
}
impl<P, N> PipelineStep<P, N>
where
P: Processable,
N: Processable<Input = P::Output>,
{
pub fn new(process: P, next: N) -> Self {
Self { process, next }
}
}
impl<P, N> Processable for PipelineStep<P, N>
where
P: Processable,
N: Processable<Input = P::Output>,
{
type Input = P::Input;
type Output = N::Output;
fn process(&self, input: Self::Input, context: &mut PipelineContext) -> Self::Output {
let output = self.process.process(input, context);
self.next.process(output, context)
}
}
pub struct EndStep<I> {
phantom: PhantomData<I>,
}
impl<I> Default for EndStep<I> {
fn default() -> Self {
Self {
phantom: PhantomData::default(),
}
}
}
impl<I> Processable for EndStep<I> {
type Input = I;
type Output = I;
fn process(&self, input: Self::Input, _context: &mut PipelineContext) -> Self::Output {
input
}
}
impl<I, O> Processable for &fn(input: I, context: &mut PipelineContext) -> O {
type Input = I;
type Output = O;
fn process(&self, input: Self::Input, context: &mut PipelineContext) -> Self::Output {
(self)(input, context)
}
}
impl<I, O> Processable for fn(input: I, context: &mut PipelineContext) -> O {
type Input = I;
type Output = O;
fn process(&self, input: Self::Input, context: &mut PipelineContext) -> Self::Output {
(self)(input, context)
}
}
pub struct ClosureProcessable<F, I, O>
where
F: Fn(I, &mut PipelineContext) -> O,
{
func: F,
phantom_i: PhantomData<I>,
}
impl<F, I, O> Processable for ClosureProcessable<F, I, O>
where
F: Fn(I, &mut PipelineContext) -> O,
{
type Input = I;
type Output = O;
fn process(&self, input: Self::Input, context: &mut PipelineContext) -> Self::Output {
(self.func)(input, context)
}
}
#[cfg(test)]
mod tests {
use crate::io::pipeline::{
ClosureProcessable, EndStep, PipelineContext, PipelineProcessor, PipelineStep, Processable,
};
use std::sync::mpsc;
pub struct DummyPipelineProcessor;
impl PipelineProcessor for DummyPipelineProcessor {}
fn add_one(input: u32, context: &mut PipelineContext) -> u8 {
input as u8 + 1
}
fn add_two(input: u8, context: &mut PipelineContext) -> u32 {
input as u32 + 2
}
#[test]
fn test() {
let mut context = PipelineContext {
};
let output: u32 = PipelineStep {
process: add_two as fn(u8, &mut PipelineContext) -> u32,
next: EndStep::default(),
}
.process(5u8, &mut context);
assert_eq!(output, 7);
let output = PipelineStep {
process: add_one as fn(u32, &mut PipelineContext) -> u8,
next: PipelineStep {
process: add_two as fn(u8, &mut PipelineContext) -> u32,
next: EndStep::default(),
},
}
.process(5u32, &mut context);
assert_eq!(output, 8);
let output: u32 = PipelineStep {
process: ClosureProcessable {
func: |input: u8, context| -> u32 {
return input as u32 + 2;
},
phantom_i: Default::default(),
},
next: EndStep::default(),
}
.process(5u8, &mut context);
assert_eq!(output, 7);
}
} |
@maxammann It's not just "right now", the definition of that trait is incompatible with Rust functions. That is, this trait definition: pub trait Processable {
type Input;
type Output;
fn process(&self, input: Self::Input, context: &mut PipelineContext) -> Self::Output;
} Suggests that every implementer of It's suitable for I didn't check if everything else is agreeable with this version, but it's the arguably correct one: pub trait Processable<I> {
type Output;
fn process(&self, input: I, context: &mut PipelineContext) -> Self::Output;
} |
So, my definition of Processable works nicely for my usecase even though it is incompstible with functions. I already tried for implement the quoted version of Processable for Fn(I) -> O. I failed because of the same reasons. The problem was that I was unable to forward the generic I from Processable to the Fn. |
This works fine:
But this:
produces a compile error:
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:
The text was updated successfully, but these errors were encountered: