Higher ranked trait bounds and associated types of type parameters


#1

I’m trying to access an associated type defined on a type parameter bounded by a trait which is in turn bounded by a higher ranked lifetime and can’t figure out how to do it correctly. Here is a toy example of what I’m trying to do:

Bad syntax, right idea:

#![feature(unboxed_closures)]

struct Apply<A, F> where F: for<'a> FnMut<(&'a A,)> {
    input: Option<A>,
    function: F,
    // This doesn't work.
    output: Option<<F as for<'a> FnMut<(&'a A,)>>::Output>,
}

Compiles and appears to work but looks funny:

#![feature(unboxed_closures)]

struct Apply<A, F> where F: for<'a> FnMut<(&'a A,)> {
    input: Option<A>,
    function: F,
    // This works but is a little funny.
    output: Option<<F as FnMut<(&'static A,)>>::Output>,
}

Is there any better way to do this?


#2

<F as for<'a> FnMut<(&'a A,)>>::Output does not make sense, what if F::Output = &'a u8?

Maybe make the return type explicit?

struct Apply<A, R, F> where F: FnMut(&A) -> R {
    input: Option<A>,
    function: F,
    output: Option<R>,
}

#3

That’s actually what I was trying to avoid (I was trying to reduce the number of type parameters in a struct). As far as I know, given some F, F::Output should be uniquely known or is that not true in this case (is F still generic in this case?).

Additionally, why does static work? If I just use static everywhere, (no for<'a>) rust is perfectly happy to accept non-static references. Furthermore, I can’t come up with an example where making the return type explicit makes a difference

Without parameter:

#![feature(unboxed_closures)]

struct Apply<A, F> where F: FnMut<(&'static A,)> {
    input: Option<A>,
    function: F,
    output: Option<<F as FnMut<(&'static A,)>>::Output>,
}

impl<A, B, F> Apply<A, F> where F: FnMut(&A) -> B {
    fn new(input: A, function: F) -> Self {
        Apply {
            input: Some(input),
            output: None,
            function: function,
        }
    }

    fn apply(&mut self) {
        if let Some(input) = self.input.take() {
            self.output = Some((self.function)(&input))
        }
    }
}

fn main() {
    {
        let mut s = String::from_str("output");
        {
            // s and inp are not static references.
            let mut app = Apply::new(true, |inp| &*s);
            app.apply();
            println!("{}", app.output.unwrap());
            println!("{}", s);
        }
        s.push('a');
        println!("here");
    }
}

With parameter:

#![feature(unboxed_closures)]

struct Apply<A, B, F> where F: FnMut(&A) -> B {
    input: Option<A>,
    function: F,
    output: Option<B>,
}

impl<A, B, F> Apply<A, B, F> where F: FnMut(&A) -> B {
    fn new(input: A, function: F) -> Self {
        Apply {
            input: Some(input),
            output: None,
            function: function,
        }
    }

    fn apply(&mut self) {
        if let Some(input) = self.input.take() {
            self.output = Some((self.function)(&input))
        }
    }
}

fn main() {
    {
        let mut s = String::from_str("output");
        {
            // s and inp are not static references.
            let mut app = Apply::new(true, |inp| &*s);
            app.apply();
            println!("{}", app.output.unwrap());
            println!("{}", s);
        }
        s.push('a');
        println!("here");
    }
}

(With optimizations enabled, the resulting assembly is identical except for the mangled names).