Is PhantomData absolutely required in this case? if so, why?

pub struct TqlGb1<T, Out, F1: Fn(&T) -> Out, F2: Fn(Out, &T) -> Out> {
    f1: F1,
    f2: F2,
}

I know that this can be solved via

_t: PhantomData<T>,
_out: PhantomData<Out>

=====

My question is: is this PhantomData absolutely required? If so, why? I feel like T, Out are already "used" in F1/F2.

1 Like

See Rust complains about unused type parameter which is only used to parameterize another · Issue #23246 · rust-lang/rust (github.com).

The possibly more straightforward alternative is to drop the Fn bounds alltogether and only apply them to impl blocks etc. (They would be needed on the impl blocks (or on the individual methods) anyways.)

So

pub struct TqlGb1<F1, F2> {
    f1: F1,
    f2: F2,
}

impl<F1, F2> TqlGb1<F1, F2>
{
    // your methods here …
    fn some_method<T, Out>(&self)
    where
        F1: Fn(&T) -> Out,
        F2: Fn(Out, &T) -> Out,
    { 
        // …
    }
}

Edit: Nevermind, it won’t work as a bound on the impl directly. But you can put it on all the methods (when it’s needed)… I fixed the code above ^^

6 Likes

The right way to go is @steffahn's suggestion, but if you can't for some reason, for right covariance, use PhantomData but with fn():

pub struct TqlGb1<T, Out, F1: Fn(&T) -> Out, F2: Fn(Out, &T) -> Out> {
    f1: F1,
    f2: F2,
    _marker: PhantomData<(fn(&T) -> Out, fn(Out, &T) -> Out)>,
}
5 Likes

This probably isn't very useful, but with the unstable Fn traits you can get rid of the Out parameter:

#![feature(unboxed_closures)]

pub struct TqlGb1<T, F1, F2>
where
    F1: for<'a> Fn<(&'a T,)>,
    F2: for<'a> Fn<(<F1 as FnOnce<(&'a T,)>>::Output, &'a T), Output=<F1 as FnOnce<(&'a T,)>>::Output>
{
    f1: F1,
    f2: F2,
    _t: PhantomData<fn(&T)>,
}
5 Likes