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

new RFC: static_lifetime_in_statics #1623

Merged
merged 5 commits into from
Aug 22, 2016

Conversation

llogiq
Copy link
Contributor

@llogiq llogiq commented May 20, 2016

Adding 'static lifetimes to every reference or generics lifetime value in static or const declarations adds no value. So this PR suggests we allow to omit them.

Rendered

@nikomatsakis
Copy link
Contributor

Seems like a reasonable idea; I've thought about proposing a change like this from time to time. There are in fact no other lifetime names in scope at all, so 'static is literally the only thing you could write -- except for within higher-ranked functions and objects.

The RFC is a bit incomplete in that it doesn't seem to spell out how this should interact.

Functions I think work today, so presumably they continue the same:

static foo: fn(&u32) -> &u32 = ...;  // for<'a> fn(&'a u32) -> &'a u32
static foo: &Fn(&u32) -> &u32 = ...; // &'static for<'a> Fn(&'a u32) -> &'a u32

For object types using <>, I presume it defaults to 'static:

trait SomeObject<'a> { ... }
static foo: &SomeObject = ...; // &'static SomeObject<'static>
static foo: &for<'a> SomeObject<'a> = ...; // &'static for<'a> SomeObject<'a>

@llogiq
Copy link
Contributor Author

llogiq commented May 20, 2016

Exactly. I will extend the Detailed Design section to include your examples.


Currently, having references in `static` and `const` declarations is cumbersome
due to having to explicitly write `&'static ..`. On the other hand anything but
static is likely either useless, unsound or both. Also the long lifetime name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I wrote, since 'static is the only name in scope, it's certainly not "unsound" -- other names are just a guaranteed compilation error. I'd just drop this sentence.

@llogiq
Copy link
Contributor Author

llogiq commented May 20, 2016

Thank you @nikomatsakis for those detailed comments!

@ticki
Copy link
Contributor

ticki commented May 20, 2016

I know this might be a little controversial, but I am for allowing full type inference in statics and consts, since it adds a nice symmetry, and makes a lot of things more ergonomic.

@llogiq
Copy link
Contributor Author

llogiq commented May 20, 2016

@ticki As I wrote in the Alternatives section, even with full type inference, having the default still allows us to declare the types thus documenting them without writing out all those 'static. On the other hand, if we only added type inference, the extra lifetime annotations would make writing and reading the types so cumbersome that users will likely have rustc infer them. So we should implement this issue especially if we'd later adopt inference.

@ticki
Copy link
Contributor

ticki commented May 20, 2016

Sure, I was merely noting. I do support this RFC.

@llogiq
Copy link
Contributor Author

llogiq commented May 20, 2016

Full inference would be great. I do however wonder two things: First, will this change give us a good etnough solution for the majority of cases (with its vastly smaller complexity)? And second, would I want to do the inference for complex statics in my head (because I cannot simply read the type if it's inferred, though IDEs could probably help with that)?

Currently I think that on balance for simple cases writing the type is relatively cheap in terms of code size, whereas with more involved types (especially with types containing closures), I'd rather see them written down as documentation. But I may be wrong.

@nikomatsakis
Copy link
Contributor

It would certainly be convenient for us to do inference, but I am wary: it
is a significant complication to the compiler and I'd not be inclined to
undertake it right now, particularly as we are still working out the
interaction of constants, types, and generics. It may be that once we've
worked through that, though, adding inference would be relatively
straightforward (I think that solving these questions nicely will require
us resolving constants more lazilly, and with some kind of live cycle
detection, which we could probably leverage to do type inference there
too).

However, as the RFC argues, even if we did type inference, I imagine some
people will want to write explicit types, and in those cases 'static
feels like a sensible default.

On Fri, May 20, 2016 at 2:37 PM, llogiq [email protected] wrote:

I think full inference would be great. I do wonder two things: First, will
this change give us a good etnough solution for the majority of cases (with
its vastly smaller complexity)? And second, would I want to do the
inference for complex statics in my head (because I cannot simply read the
type if it's inferred, though IDEs could probably help with that)?

Currently I think that on balance for simple cases writing the type is
relatively cheap in terms of code size, whereas with more involved types
(especially with types containing closures), I'd rather see them written
down as documentation. But I may be wrong.


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#1623 (comment)

@birkenfeld
Copy link

I think symmetry with function signatures and other items (public API is explicitly typed) is more important than symmetry with let bindings.

What I sometimes wish for is inference of the length for array types, i.e. being able to write const X: [T; _] = [...].

@ticki
Copy link
Contributor

ticki commented May 20, 2016

@nikomatsakis, I certainly agree that there are priorities, and it will likely increase complexity (although I can think of multiple ways to attack this). The change described in this RFC is relatively simple.

@birkenfeld, I disagree. static and const can both be thought of as global variables, and thus in the same class as let bindings.

@llogiq
Copy link
Contributor Author

llogiq commented May 20, 2016

@ticki One difference between let bindings and static/const declarations is that the latter two can appear and be used anywhere in the code, whereas the former is confined to its scope. The locality is what changes the balance in favor or against inference, not the declaration semantics.

@ticki
Copy link
Contributor

ticki commented May 20, 2016

Yes, but the fundamental concept is the same: they're both value declarations associated with a particular identifier.

@birkenfeld
Copy link

@birkenfeld, I disagree. static and const can both be thought of as global variables, and thus in the same class as let bindings.

I don't dispute that. But there isn't only one "class" in which these things can be placed - and I happen to think that consistency within another class - item-level signatures - is more important.

@petrochenkov
Copy link
Contributor

Yes, but the fundamental concept is the same: they're both value declarations associated with a particular identifier.

There are a bunch of "fundamental concepts" lying around here - global vs local, interface vs implementation detail. Not all of them make local variables and constants/statics look similar.

@llogiq
Copy link
Contributor Author

llogiq commented May 20, 2016

@ticki I agree that the fundamental concept is the same. I just argue that this is beside the point, because unlike let bindings which have high locality, globals can be all over the place. Consider the following snippet:

static FOO = &[BAR];
// 100 lines of code here
static BAR = ... // whatever
// another 100 lines of code using BAR
.. x == FOO

and now let's say someone changes BAR. The error will appear with the FOO definition and use, though the change was somewhere else. With written types, the error will be at the declaration at fault.

@Ericson2314
Copy link
Contributor

If we ever got contravariance back,

type Foo<'a> = fn(&'a Bar);
static BAZ: Foo = ...;
// BAZ: Foo<'static>

might be a bit weird. Otherwise I like this.

@llogiq
Copy link
Contributor Author

llogiq commented May 22, 2016

I'm not sure if we can differentiate between function argument and reference lifetimes. If we could, defaulting only the latter while leaving the former generic would be the best solution.

@glaebhoerl
Copy link
Contributor

The same issue already exists for lifetime elision so there's plenty of precedent.

@huonw
Copy link
Member

huonw commented May 22, 2016

Another thing that could allow non-'static lifetimes is associated constants, e.g.:

trait Foo<T> { const X: T; }

impl<'a> Foo<&'a u8> for &'a i32 {
    const X: &'a u8 = &0;
}

This doesn't seem particularly problematic on the face of it (especially given associated constants are unstable), but I haven't devoted much thought to it.

@llogiq
Copy link
Contributor Author

llogiq commented May 22, 2016

@Ericson2314 @glaebhoerl @huonw I think that's the exact spot where we can strengthen the RFC: By defining the interaction between the 'static default and lifetime elision. My currently favorite solution would be to elide function lifetimes and default reference lifetimes to 'static. This distinction would be carried over into generics. For example, if we have

struct SomeInnerType<'a> {
    foo: &'a [u32]
}

struct SomeType<'a, 'b, 'c: 'a> {
    x: &'a SomeInnerTyp<'c>,
    y: Fn<'b>(&'b Foo) -> &'b Bar
}

we could make a static some_instance : &SomeType = .. which would actually be of type &'static SomeType<'static, 'a, 'static> (so some_instance.x would be a &'static SomeInnerType<'static>, whereas some_instance.y would be a Fn<'a>(&'a Foo) -> &'a Bar).

Note that this would make a difference based on the internals of SomeInnerType. I believe this is acceptable, because it appears to do the right thing in all cases I've come up with so far.

Am I right or did I miss some corner case? (Edit: Fixed the wrong type)

@arielb1
Copy link
Contributor

arielb1 commented May 22, 2016

@llogiq

You can't have for<'a> SomeType<'static, 'a, 'static> because SomeType is not a function or trait.

@notriddle
Copy link
Contributor

notriddle commented May 24, 2016

@TimNN
Copy link
Contributor

TimNN commented May 24, 2016

@notriddle: If I understand the RFC correctly, it explicitly talks about your example (emphasis mine):

"The 'static default does not override lifetime elision in function signatures"

@notriddle
Copy link
Contributor

I meant to reply to @llogiq. Sorry about the confusion.

@llogiq
Copy link
Contributor Author

llogiq commented May 24, 2016

@notriddle The confusion is obviously mine. Thanks for helping to clear it up.

@llogiq
Copy link
Contributor Author

llogiq commented May 24, 2016

Now I extended the Design section to hopefully clear up the confusion by describing interaction with a few other features.

@Ericson2314
Copy link
Contributor

@nikomatsakis true. I guess my concern is we'd be providing sugar for something the user probably doesn't intend in that situation, but maybe it's better to be simple and consistent. We could instead just add a lint for inferred 'static in would-be contravariant position.

@donaldpipowitch
Copy link

That sounds nice. As a beginner I just stumbled over this a little bit.

@nikomatsakis
Copy link
Contributor

Hear ye, hear ye! This RFC is now entering final comment period. The @rust-lang/lang team is currently inclined to accept the RFC.

The conversation focused on a few points of clarification (e.g., what happens to anonymous bound regions in a fn type signature). It was also pointed out that while 'static is a good default for statics and constants, it is not such a good choice for associated constants, which still require a fully explicit type (seems fine). It may also be a problem in the future if we add lifetime-parameterized modules, but in such a case we can simply disable the default in that context (or address in some other way).

@nikomatsakis nikomatsakis added the final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. label Aug 12, 2016
@nrc
Copy link
Member

nrc commented Aug 18, 2016

+1

@nikomatsakis
Copy link
Contributor

Huzzah! The @rust-lang/lang team has decided to accept this RFC.

@nikomatsakis nikomatsakis removed the final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. label Aug 22, 2016
@nikomatsakis nikomatsakis merged commit 2526483 into rust-lang:master Aug 22, 2016
@llogiq llogiq deleted the static_statics branch August 22, 2016 19:06
@Centril Centril added A-typesystem Type system related proposals & ideas A-inference Type inference related proposals & ideas A-const Proposals relating to const items labels Nov 23, 2018
@Boscop
Copy link

Boscop commented Nov 25, 2018

@nikomatsakis

  1. Can we at least allow lifetime elision for associated constants in the impls of traits? So that we don't have to name the lifetime in every impl, e.g.:
pub trait Endpoint {
	const ENDPOINT: &'static str;
	// ...
}

impl Endpoint for UserList {
	const ENDPOINT: &str = "user/list";
	// ...
}

impl Endpoint for UserEdit {
	const ENDPOINT: &str = "user/edit";
	// ...
}
  1. Could we maybe also allow omitting the type for associated constants in the impls?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-const Proposals relating to const items A-inference Type inference related proposals & ideas A-typesystem Type system related proposals & ideas T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.