struct Cacher<T,P,R>
where T : Fn(P) -> R
{
calculation : T,
value : Option<R>
}
The compiler complains about 'P' and says 'unused type parameter'.
If i remove the 'P' the compiler complains again and says 'cannot find type P in this scope'
How can I keep 'P' and stop the compiler to complain?
Does it change the struct memory layout? (Edit: nevermind, 'Zero-sized type used to mark things' )
Why the compiler is not able to see that P is used? It's very odd to add a member that is not intent to be used to stop the compiler to complains.
I'm not completely familiar with the specifics, so all this is just what I've come to understand, but it has to do with variance checking. If you had a T which implemented both Fn(&u32) -> R and Fn(&mut u32) -> R, which P would it pick? You have to have something in the struct to pin that down, so you add a marker member that says which P you've picked for that instance of the struct.
Variance has to do with lifetimes, so a better example would be: if you have a function that takes Cacher<_, &'a u32, _> and you have an object that is Cacher<_, &'b u32, _>, can you pass object to function?
If Cacher is covariant in T, you can only pass object to function if 'b: 'a
If Cacher is contravariant in T, you can only pass it if 'a: 'b
If Cacher is invariant in T, you can only pass it if 'a: 'band'b: 'a
Traits are always invariant, but T can implement a whole family of Fn traits (e.g. for<'a> Fn(&'a u32)), so the trait bound itself doesn't give enough information to conclude that Cacher should be contravariant in T. A function is contravariant in its argument types, which is why PhantomData<fn(T)> is the correct choice here.