Open mejrs opened 2 years ago
I've arrived at this chapter of the Rustonomicon a number of times when I've really wanted to try and clarify how to use the variance terms correctly when talking about Rust types and lifetimes and I have to admit I've found the chapter quite confusing, especially at the point where it starts talking about lifetimes.
I think the chapter sets you up for confusion by focusing on the concept of "subtypes" and "supertypes", and this note adds to that confusion imho:
NOTE: The typed-ness of lifetimes is a fairly arbitrary construct that some disagree with. However it simplifies our analysis to treat lifetimes and types uniformly.
because it sort of implies that only a minority disagree with the typed-ness of lifetimes - and once you read further I find it hard to agree that "it simplifies our analysis to treat lifetimes and types uniformly."
Digging back into some of the past discussions about lifetime variance, and some of the confusion that even existed within the compiler I found these two issues that seem very relevant here:
https://github.com/rust-lang/rfcs/issues/391 https://github.com/rust-lang/rust/issues/15699
Both of those seem conclude that it would be best to avoid thinking of lifetime variance in terms of subtyping and instead focus on the "outlives" relationship.
This paragraph in the rustonomicon shows there's an awareness of this source of confusion:
This is a large source of confusion, because it seems backwards to many: the bigger region is a subtype of the smaller region. But it makes sense if you consider our Animal example: Cat is an Animal and more, just as 'big is 'small
but the idea that that a bigger lifetime is a "subtype" based on thinking of it as a smaller region "and more" - like how a Cat is an Animal "and more" really doesn't make much sense to me personally. It seems like the rustonomicon is currently trying too hard to impart an intuitive understanding of lifetime variance in terms of subtyping that hinges on this "and more" idea, but at least for me this has never felt intuitive.
I have a feeling that the explanation of lifetime variance could be clearer if it was upfront about variance for lifetimes being based on a different kind of "outlives" relationship which isn't comparable to subtyping.
At least for me, based on reading the issues above I feel like things are clearer if I don't fight to think about how lifetime variance is like subtyping at all - and just thinking of it as having it's own rules, based on an "outlives" relationship.
Today in the Rust community discord we had a discussion about variance and the nomicon chapter discussing it (starting here), and how it's generally confusing and is something that just doesn't "click" for a lot of people (myself included).
First impression
I'll start off by summarizing my (and others') impressions as we read the chapter.
Uh, ok. This tells nothing about what subtyping is supposed to mean. (it's much later that this is actually explained)
Well I don't know what "subtyping in other languages" is, either.
Seems kinda random how we got here. Why are we introducing a new language? I'm interested in what variance is in Rust and why I should care about it. This is anything but simple.
At this point the article has completely lost me.
Constructive feedback
There are some things that could be done better:
&'static Foo
tofn bar<'a>(&'a Foo))
, then show an issue with doing that (adapt the "meowing dogs" example to lifetimes) and then introduce variance to deal with this.Finally I would recommend you to read https://viralinstruction.com/posts/defense/. The author's frustration with teaching classes and inheritance is how I feel w/r to subtyping and variance.