Shower thought:
Consider a method with a signature like
// lifetimes shown for clarity fn method<'a>(&'a self) -> TypeWithLifetime<'a>;
The borrow-checker considers the
TypeWithLifetime<'a>
to extend the "loan" associated with&'a self
for as long as theTypeWithLifetime
is alive, preventing anything else from mutably borrowingself
during that time.But why? From the signature alone, there's no way of telling why this lifetime appears in
TypeWithLifetime
, so who says it carries anything with that lifetime? Does the compiler simply assume that every lifetime in the output of a function must extend borrows with those lifetimes from the input arguments?Are there cases where this does not occur? Perhaps if...
...perhaps ifTypeWithLifetime<'a>
is contravariant over'a
?
Indeed, after testing it, it appears that this is the case.
Here's some types with variance types of various types of variance:
type Cov<'a> = &'a ();
type Contra<'a> = fn(&'a ());
// compile-time tests just to make sure
fn verify_covariance<'a, 'b:'a>(c: Cov<'b>) -> Cov<'a> { c }
fn verify_contravariance<'a, 'b:'a>(c: Contra<'a>) -> Contra<'b> { c }
// and some methods
struct Struct;
impl Struct {
fn cov_mut(&mut self) -> Cov { &() }
fn contra_mut(&mut self) -> Contra { |_| () }
}
We can see that cov_mut
and contra_mut
are held to different standards by the borrow checker:
fn main() {
let mut x = Struct;
{
// This is forbidden. `a` and `b` both borrow from `x`.
let a = x.cov_mut();
let b = x.cov_mut();
}
{
// This is okay. `a` and `b` do not borrow from `x`.
let _a = x.contra_mut();
let _b = x.contra_mut();
}
}
Unless I am misinterpreting these results, the borrow checker indeed uses variance to determine whether a returned type extends the borrow.
This appeals to my intuition as a PL enthusiast; there is some inherent connection between covariance and the availability of data (i.e. supplying some type T
), while contravariance tends to indicate a sort of "data hole" (i.e. demanding some type T
). But I'm still surprised to see the borrow checker make this connection, and I almost can't help but wonder if there is some way to fool it...
Any thoughts on this?