So, consider this type &'a mut T where T = &'b U; The second row says T is invariant in &'a mut T, that is, &'b U is invariant in &'a mut &'b U, however, the first row instead says 'b and U is covariant in &'b U, so, eventually, what the variance of 'b and U in &'a mut &'b U? Is it specified by first row or second row? How to determine the variance if we meet such a composite type with combined variance?
Sorry, that was abrupt. [1] As per the "transform" table, The U in &'a mut &'b U is
(invariant) × (covariant) = (invariant)
Since primitives don't compose, the only relevance of their bivariance is for the GLB, which basically says they're inert. I believe there's technically bivariance elsewhere in the language (generic otherwise-unconstrained associated type parameters), but I'm not sure it can be demonstrated.
So generally you can ignore it as a no-op,[2] and the remaining rules are
In composition
invariance persists
covariance is the identity
contravariance "flips" the other
In GLB
covariance + covariance = covariance
contravariance + contravariance = contravariance
anything else is invariant (including: invariance persists)
Oh, what does the variance on the left side of x and that on the right side of x correspond to in the table?
For example, V x W, Do we find V in the first element in each row? Or find V in the first element in each column?
Moreover, for a compound type, Is V the variance of the outermost type? For example:
fn(&'_ i32);
The outermost type is fn(T), in order to determine the variance of i32 in the type, we say fn(T) is contravariant over T, so V designates the variance of T? Then &'_ i32 is covariant over i32, hence W means covariant, hence the variance of i32 in the complete type is calculated by contravariant x covariant, where contravariant corresponds to the symbol in the first element of the fourth row and covariant corresponds to the symbol in the first element of the third column, the result is -, so it is contravariant?
If we continue to wrap an fn in the outside of the above type, namely, fn(fn(&'_ i32)), now the variance of i32 is the result of contravariant * contravariant, which is covariant?
That code has a bug - it neglects the match case for (Variance::Bivariant, Variance::Invariant)
Wait, no - I have the ordering flipped. (Variance::Invariant, Variance::Bivariant) is the covered case. The chart prefers to keep the left side in both In- and Bi- variant cases.
In modern C/C++ that's probably correct. But by the time const ptrs were added to those 2 languages, I'd long since stopped programming in them due to the fractal footgun that is memory unsafety (even with things like shared_ptr etc).
Since it's after my time (so to speak) that could never have been a frame of reference for me
To this date, C types syntax always confus… well, let’s look this up! => “pointer to pointer to const void”
I suppose that checks out of being - a least roughly - “&mut &T”
(ignoring nullability, and general memory safety, as well as the fact that there’s a generic T here, and last but not least the fact that const * in C/C++ does not mean immutable but just “you’re not supposed (allowed?) to mutate it but others could, at any time”, …)
const* in C++ means you can't modify without casting, but void requires casting anyway. It's incredibly bad form to modify const and in some cases is UB (e.g. if it happens to point to a static const).