Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Paragraph about T: 'static is not right #31

Open
vojtechkral opened this issue Apr 8, 2021 · 8 comments
Open

Paragraph about T: 'static is not right #31

vojtechkral opened this issue Apr 8, 2021 · 8 comments

Comments

@vojtechkral
Copy link

vojtechkral commented Apr 8, 2021

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
Copy link
Author

vojtechkral commented Apr 8, 2021

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

  • T does not have to be valid for the entire program

    • It does, the type has to be valid for the entire program, but this is unrelated to values of that type. Those might or might not exist at arbitrary times.
  • can have lifetimes of different durations

    • If a type T is static, then either it has no lifetime parameters at all, or it has, but they are instantiated as 'static. Values of T may have arbitrary lfietimes, but this is inrelated to the type itself.

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

@vojtechkral
Copy link
Author

vojtechkral commented Apr 9, 2021

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 😁

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
Copy link

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
Copy link
Author

vojtechkral commented Apr 9, 2021

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
Copy link

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

  • T: U iff T is a subtype of U.
  • 'a: 'b iff 'a is a subytpe of 'b.

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
Copy link
Author

vojtechkral commented Apr 9, 2021

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
Copy link
Owner

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
Copy link
Author

vojtechkral commented Apr 13, 2021

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 🙂

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants