In Rust it ends up actually being pretty clean because mutability is inherited. Generic values are covariant because (as you note), they're by definition unshared. &T is covariant in T because you can't mutate T through an &. &mut T is invariant in T because you can mutate through it.
This therefore enables one to pass &Collection<T> where &Collection<U> is expected if T is a subtype of U, because we know the collection only supports "read only" operations. It's been a while since I've used C++ and never really touched const stuff, but if it works like Rust, that means a `Vector<T> &const` could be soundly covariant. It wouldn't work if you could get `T*`'s out of a `Vector<T> &const`.
That said, we don't have OOP-style inheritance in Rust, so variance only applies to lifetimes. This allows you to pass an &LongLived where an &ShortLived is expected.
I give a basic rundown here: https://doc.rust-lang.org/nightly/nomicon/subtyping.html
Note that I deviate from the academic standard and simply use "variance" for covariance, because the academic terms are dumb and confusing (so many Rust devs, even with PL backgrounds, were constantly getting confused when we used that terminology). Also you don't really need to care about contravariance anyway because it's also dumb and confusing (as this post notes).