pretzelhammer / rust-blog

Educational blog posts for Rust beginners
Apache License 2.0
7.57k stars 400 forks source link

Paragraph about `T: 'static` is not right #31

Closed vojtechkral closed 2 months ago

vojtechkral commented 3 years ago

TL;DR The misconception that if T: 'static then T must be valid for the entire program ... is not a misconception. It's literally true, in a sort of pedantic way.

The problem is that people conflate validity of types with lifetime of values. The fact that a type is valid for an entire duration of a program is disctinct from whether or not there are values of that type physically existing at some point or another.

A type T: 'static is valid even before you create any values of it, in fact, it's valid even if it's absolutely impossible to create any values of it, such as with the ! or Infallible or similar types. On the other hand, a non-static type can only be referred to in some part of a program where the type contains some specific lifetime, which only exists in that part of the prorgam. Again, this is distinct from any actual values of that type - those might not exist either.

As a consequence, leaking is completely unrelated to whether a type is 'static or not, since leaking deals with values. You can leak values of non-static types just fine. I'm afraid mentioning leaking in that section only increases the confusion.

Here's a demonstration of this:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=3615f118612c24891f44e2e0c820d3a5

Hopefully this'll make sense. It's kind of hard to show this in an actual code, since I don't think there's a way of implementing functions for a non-static type only (there's no non-static marker trait etc.).

vojtechkral commented 3 years ago

As a consequence, I'd be careful with these takeaways:

(Editing as I'm trying to make this sound less pedantic / annoying / etc., not sure if that's a succes, sorry! :grimacing:)

vojtechkral commented 3 years ago

I absolutely agree with the blogpost that T: 'static shouldn't be read as "T has a 'static lifetime", that's indeed pretty misleading. However, I don't believe reading it as "T is bounded by a 'static lifetime" is a good idea either, because the bound actually goes in the other direction :grin:

The syntax 'a: 'b means "'a lives at least as long as 'b", ie. 'b is bounded by 'a, not the other way around.

Thereby, the syntax T: 'static literally means "Type T exists at least as long as the 'static lifetime" (where "Type T" really is just talking about the type, not values of it).

Personally I read T: 'static as "T is a static type".

lebensterben commented 3 years ago

The syntax 'a: 'b means "'a lives at least as long as 'b", ie. 'b is bounded by 'a, not the other way around.

Bound in this context should not be confused with bound as in upperbound/lowerbounded. That is, 'a: 'b or 'a bounded by 'b doesn't mean lifetime 'a is upperbounded by lifetime 'b.

Bound in this context should be understood as bound as in bounded set. That is, 'a: 'b or 'a bounded by 'b means 'a is a subtype of 'b. Representing them in Venn Diagram, the subtype 'a is within the supertype 'b, thus 'a is bounded by 'b.

vojtechkral commented 3 years ago

Representing them in Venn Diagram, the subtype 'a is within the supertype 'b, thus 'a is bounded by 'b.

What would be the meaning of such a Venn diagram?

If you wanted to make, for example, a Venn diagram of code lines / locations where a lifetime is valid, then it would be exactly the other way around - 'b would be within 'a (hence 'a "outlives" 'b as the reference puts it)

lebensterben commented 3 years ago

it would be exactly the other way around - 'b would be within 'a (hence 'a "outlives" 'b as the reference puts it)

'a outlives 'b thus 'a is a smaller set in the Venn diagram, which is bounded by a larger set 'b.

See https://doc.rust-lang.org/nomicon/subtyping.html?highlight=subtyp#subtyping-and-variance

Representing the subtype and supertype as sets as in set theory, it's clear that the set of subtype must be a subset of the set of supertype. In Venn Diagram, subset is contained insides superset.

That's the correct way to understand bound.

vojtechkral commented 3 years ago

Oh, I see what you mean. The set of types B: 'b is a subset of the types A: 'a. You're right.

Still not sure about the "bound" wording, in context of Rust by "bound" I usually understand the whole clause 'a: 'b or any other such clause.

pretzelhammer commented 3 years ago

The problem is that people conflate validity of types with lifetime of values.

If the reader interprets "If T: 'static then T must be valid for the entire program" as the former then it's probably inaccurate, but if they interpret it as the latter then it is accurate, so I guess it's arguably ambiguous. If I changed the phrasing to be "If T: 'static then values of type T must be valid for the entire program" would you consider this issue fixed?

Thereby, the syntax T: 'static literally means "Type T exists at least as long as the 'static lifetime"

That's not true though, I can create and drop a String before the end of the program, and String: 'static. Nobody would say that all Strings have 'static lifetimes though.

We could say "if T: 'static then T CAN live at least as long as 'static" but does that mental model still work for 'b: 'a? Actually, it kinda does, although most Rust material describes the latter as "'b outlives 'a" the following is also technically true: "'b CAN live at least as long as 'a".

Hmmm, I'm gonna think about the phrasing some more.

vojtechkral commented 3 years ago

If I changed the phrasing to be "If T: 'static then values of type T must be valid for the entire program" would you consider this issue fixed?

Yeah, that seems perfectly fine :slightly_smiling_face:

That's not true though, I can create and drop a String before the end of the program, and String: 'static. Nobody would say that all Strings have 'static lifetimes though.

Well that's right Strings indeed don't have 'static lifetimes, the String type has no lifetimes. References to such values contain lifetimes (and thereby their type exist for a limited time unlike the String's)...

We could say "if T: 'static then T CAN live at least as long as 'static" but does that mental model still work for 'b: 'a? Actually, it kinda does, although most Rust material describes the latter as "'b outlives 'a" the following is also technically true: "'b CAN live at least as long as 'a".

Yeah, the "outlives" terminology is kind of confusing since it's actually inclusive (ie. it's a >=, not only >). In fact, if T: 'static, then it implies that T must exists at least "as long as" 'static (and since there's no longer lifetime than 'static, this defaults to equivalence) and that implies that values of T might exist that long as well, in theory. For example, you can do this:

use std::mem::MaybeUninit;

static FOO: MaybeUninit<String> = MaybeUninit::uninit();

This wouldn't be possible if String (as a type) weren't 'static. The value isn't going to be initialized properly of course, but that's a technical issue... Maybe if the OS provided some preallocated heap space, this could be done 'for real'. But whatever, what matters here is that it type-checks.

Thanks for looking into this.

pretzelhammer commented 2 months ago

although official rust documentation and even the rust compiler refers to lifetime bounds as... well... "lifetimes bounds", i agree that saying "T is bounded by 'static" is kinda confusing for people to read and "T can live at least as long as 'static" is much clearer, so i've made those changes in this commit: https://github.com/pretzelhammer/rust-blog/commit/ed95bcbf7f0efdbd20d94d5e77b9870b9d2b72e7

i know i said earlier that i would change the name of the section to "If T: 'static then values of type T must be valid for the entire program" but i actually can't do that since it would break people's links to that section (github derives the hash links from the document's headings), so the title will have to stay as it is currently, which i think is fine, since everyone reading the article more-or-less gets what i'm saying anyway