What is the variance in a user-defined struct type?

In neither reference nor nomicon can I find the specification regarding the variance in a user-defined type.

Consider this example:

struct Data<'a>(&'a i32);
fn test_lifetime<'a>(f:&'a i32,v2:& Data<'a>){}

fn main(){
	let i0 = 0;
	let mut d = Data(&i0);
	{
		let i = 0;
		test_lifetime(&i, & d);  // assume `&i` is `&'a i`
	}
	d;  // ensure `d` has a larger region, called 'b
}

First, 'b:'a if the lifetime for &i is 'a. The above-mentioned documents say

&'a T is covariant in its T

Hence, given a type U, &'_ U can be coerced to &'_ T if U is a subtype of T. In my case, U is Data<'b> while T is Data<'a>. I don't find the specification that says a user-defined struct type is covariant in its lifetime parameter, which would give the relation that Data<'b> is a subtype of Data<'a> if 'b:'a.

That's because it isn't. It has the same variance as the usage of that lifetime parameter. Data uses this parameter by passing it into a shared reference, which is covariant in that lifetime, therefore Data is covariant in it, too.

> It has the same variance as the *usage* of that lifetime parameter.

I don't think so. when we pass &i as the first argument of test_lifetime, the largest region where &i can be valid is the block scope, we called it 'inner. Obviously, the lifetime in the type of d should have a larger region than 'inner, which is called 'b because we can use d outside of the block scope. Now, we know the type of d should be Data<'b>, the lifetime parameter of test_lifetime at most is inferred to 'inner, which means we have the signature of test_lifetime

fn test_lifetime(f:&'inner i32, v2:&'_ Data<'inner>). 

From & Data<'b> to & Data<'inner>, there must be coercion occurs.

Data uses this parameter by passing it into a shared reference, which is covariant in that lifetime, therefore Data is covariant in it, too.

Yes, I know &'a T is covariant in its 'a. However, there is no rule that says Data<'b> will be covariant in 'b if its field is so.

Quoting the reference:

The variance of other struct , enum , and union types is decided by looking at the variance of the types of their fields. If the parameter is used in positions with different variances then the parameter is invariant.

And it's possible to modify your example to get an error due to invariance of Cell<T> in T - namely, this:

use core::cell::Cell;

struct Data<'a>(Cell<&'a i32>);
fn test_lifetime<'a>(_: &'a i32, _: &Data<'a>) {}

fn main() {
    let i0 = 0;
    let d = Data(Cell::new(&i0));
    {
        let i = 42;
        test_lifetime(&i, &d); // assume `&i` is `&'a i`
    }
    let _ = d.0.get(); // use `&'b i0` stored in `Data`
}
3 Likes

Thanks, I see. I found that rule.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.