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

RFC: In-band lifetime bindings #2115

Merged
merged 8 commits into from
Sep 12, 2017
Merged

Conversation

aturon
Copy link
Member

@aturon aturon commented Aug 17, 2017

NOTE: Updated summary, 2017-09-05

Eliminate the need for separately binding lifetime parameters in fn definitions and impl headers, so that instead of writing:

fn two_args<'b>(arg1: &Foo, arg2: &'b Bar) -> &'b Baz
fn two_lifetimes<'a, 'b>(arg1: &'a Foo, arg2: &'b Bar) -> &'a Quux<'b>

fn nested_lifetime<'inner>(arg: &&'inner Foo) -> &'inner Bar
fn outer_lifetime<'outer>(arg: &'outer &Foo) -> &'outer Bar

you can write:

fn two_args(arg1: &Foo, arg2: &'b Bar) -> &'b Baz
fn two_lifetimes(arg1: &'a Foo, arg2: &'b Bar) -> &'a Quux<'b>

fn nested_lifetime(arg: &&'inner Foo) -> &'inner Bar
fn outer_lifetime(arg: &'outer &Foo) -> &'outer Bar

Lint against leaving off lifetime parameters in structs (like Ref or Iter), instead nudging people to use explicit lifetimes in this case (but leveraging the other improvements to make it ergonomic to do so).

The changes, in summary, are:

  • A signature is taken to bind any lifetimes it mentions that are not already bound.
  • A style lint checks that lifetimes bound in impl headers are multiple characters long, to reduce potential confusion with lifetimes bound within functions. (There are some additional, less important lints proposed as well.)
  • You can write '_ to explicitly elide a lifetime, and it is deprecated to entirely leave off lifetime arguments for non-& types

This RFC does not introduce any breaking changes.

Rendered

@aturon aturon added Ergonomics Initiative Part of the ergonomics initiative T-lang Relevant to the language team, which will review and decide on the RFC. labels Aug 17, 2017
@aturon aturon self-assigned this Aug 17, 2017
@nikomatsakis
Copy link
Contributor

So, I am in favor of this change in direction -- I feel like the previous efforts that were discussed on the internals board (e.g., Foo<'>) were leading in circles and not arriving at a very satisfying point. This proposal seems to achieve the goals of that effort in a better way.

Interestingly, the initial design of Rust's lifetimes worked exactly in this fashion: They were never explicitly declared, and a lifetime name was always scoped to the outermost binding scope in which it appeared. We scrapped this as part of a general move to make lifetimes more explicit. I think this reformulation addresses many of the concerns that arose in the earlier versions (in particular, that early version of Rust didn't support _ for avoiding names on lifetimes, it didn't require that lifetime parameters be declared on structs, etc).

One thing I am not sure about: I am very much in favor of the convention around capitalizing lifetime names in impls, but I am not sure whether this should be a hard rule or a lint. The compiler itself does not, I believe, need to rely on the capitalization convention -- it is more of a way to avoid "accidental" capture where a throw-away lifetime like 'a is used on the impl and then used (again) in a some distant fn. It seems like making it a lint is more consistent with our general philosophy around case conventions -- though, on the other hand, we tend to be stricter around lifetimes (e.g., disallowing shadowing) precisely because people are less familiar and hence confusion has a higher cost.

@repax
Copy link

repax commented Aug 17, 2017

I like a lot in this RFC. I'd also love being able to name lifetimes after arguments:

fn two_args(arg1: &Foo, arg2: &Bar) -> &'arg2 Baz;

@ssokolow
Copy link

I'm a little concerned about the lowercase vs. uppercase distinction.

It feels like something that would have trouble sticking in the learner's mind. (As evidenced by the fact that I didn't sleep well last night and it's the one big thing in the RFC that I just can't seem to remember as soon as I stop looking at the text of the RFC.)

I wouldn't feel comfortable with it unless there's a lint which would let me learn as I go.

@spernsteiner
Copy link

_ as a wildcard lifetime interacts badly with default type parameters:

struct S<'a, T, U=()> { ... }
let x: S<_, i32> = ...;
fn f(...) -> S<_, i32> { ... }

The type signature on x must expand to S<'_, _, i32> for backward compatibility. But users of the new syntax would likely prefer the return type of f to expand to S<'_, i32, ()>.

@rpjohnst
Copy link

rpjohnst commented Aug 18, 2017

I love this way of introducing lifetimes! It's a lot cleaner and more familiar than reusing parameter names.

I would prefer '_ over _. It makes it easier to recognize as an unnamed lifetime- _ is often used for unnamed variables instead, and this would be the only time it's used for lifetimes.

I'm also not a fan of the case distinction. In languages with similar rules around how type variables are introduced, case is often used to mark concrete types vs type variables. On the other hand, this RFC uses them to mark levels of nesting. Further, there are only two cases we can really use here, while there are at least three levels of nesting- impl, fn, and for.

We already error on shadowing lifetime names, so no existing code would be affected by simply removing the <'a>-style bindings. New/modified code might trip and accidentally restrict a lifetime in an unintended way, but that's already possible when accidentally leaving out <'a>-style declarations.

@nikomatsakis
Copy link
Contributor

_ as a wildcard lifetime interacts badly with default type parameters:

Interesting, yeah -- @aturon and I were discussing whether it was unambiguous, but we didn't think about default type parameters. This might of course be something we can address with epochs -- after all, the goal after this RFC is that one should always include _ for lifetimes -- but it may also be worth thinking about '_ or some other syntax.

I think initially @aturon wanted to just use throw-away names but I wasn't happy with that. In particular, I would like to be able to lint that every lifetime name should be used in more than one place (or else you ought to just use _). I think lints like this are spectacularly effective at preventing typos and thus mitigating the risk of not declaring lifetime names with an explicit binder.

@aturon
Copy link
Member Author

aturon commented Aug 18, 2017

@rpjohnst

We already error on shadowing lifetime names, so no existing code would be affected by simply removing the <'a>-style bindings.

Yes, that's true. But the problem is that the current lint on shadowing would become impossible; if you happened to reuse a name within fn, it'd always refer to the outer impl name (which may be a bit far away in the code). But maybe that particular footgun isn't significant enough to warrant this shift.

What did you think about something like outer('a)?

@nikomatsakis
Copy link
Contributor

Further, there are only two cases we can really use here, while there are at least three levels of nesting- impl, fn, and for.

It may be that it's better to drop this idea of a "impl-naming convention" and introduce it later if we find that there is a need for it. That said, what concerns me most is the idea that some code that is "off your screen" might use the same name as you and you wind up with accidental capture. This seems more likely to arise between impls and function signatures than around the use of fn types, but it could certainly arise in either case. For example you might have a local variable:

fn foo(x: &'a u8) {
  // ... more than a screenful of code here ...
  let x: fn(&'a u8) -> &'a u8 = ...;
}

This case in particular feels like we could likely address it with custom error messages -- since it is guaranteed that either (a) the code doesn't compile this way or (b) who cares, since the type -- while not as general as it could be -- was good enough. In the case of impls, the compilation errors are a bit trickier because they stretch across functions, and if the code compiles you might just wind up with a different API than the one you thought you had. I'm not sure.

@TimNN
Copy link
Contributor

TimNN commented Aug 18, 2017

I like the general direction of this RFC, however I have one major concern: Making lifetimes the first and so far only place where rustc attaches semantic meaning to the casing of idents sounds like a very bad idea to me. I also think that the rules regarding lifetime classification are currently underspecified:

  • "It is deprecated to use any lifetime variables that do not begin with a capital letter [in impl headers]": So a lifetime starting with _ would be treated as "lowercase"? What about _0 or _A? How does this interact with the non_ascii_idents feature for languages with no distinction between lowercase & uppercase letters? (Technically the quoted sentence answers all those questions, but I think that section should be more explicit) (Live Edit: I just noticed that you mention languages without case distinction in the drawbacks -- I think they should be given further consideration in this RFC, especially since I think that language specific lifetime classification rules are a very bad idea).
  • Would fn foo<T: 'a>(&'a T) continue to work?

@rpjohnst
Copy link

What did you think about something like outer('a)?

So, looking through the projects I work on, most impl blocks seem to use either impl-level parameters, or fn-level parameters, almost exclusively. I don't know how typical that is, but the cases that use primarily impl-level parameters would get a lot more verbose with something like outer('a), for not much gain.

I suspect the error messages for accidental capture wouldn't be that bad anyway- the fact that they would refer to the outer impl block as the definition point for the lifetime should tip you off that you've accidentally shadowed a name.

@TimNN
Copy link
Contributor

TimNN commented Aug 18, 2017

What did you think about something like outer('a)?

Just to be sure, how would that be used? Would I actually write fn foo(&self, &'outer('a));? That seems much to cumbersome to me.

Also, since we are discussing syntax, what about using "a1 for impl lifetimes and 'a for inner lifetimes.

1: I'm not sure if I actually want that


- If a lowercase lifetime variable occurs anywhere in the signature, it is
*always bound* by the function (as if it were in the `<>` bindings).
- If an uppercase lifetime variable occurs, it is *always a reference* to a
Copy link
Member

Choose a reason for hiding this comment

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

always is a strong word: I guess you want to implement it through an error that can't be turned off?

Generally I'm all for enforcing naming rules more strongly, but I think enforcement should be consistent, so IMO we should add this as warn-by-default lint to the bad_style lint group like the other lints that concern naming styles.

@phaylon
Copy link

phaylon commented Aug 18, 2017

I'm wondering if there should be some examples exploring what things look like when they involve 'static. For example:

impl<T> VecIter2<'Vec, 'static, T> { ... }

You could name that one 'STATIC to be more naming consistent:

impl<T> VecIter2<'Vec, 'STATIC, T> { ... }

@sophiajt
Copy link
Contributor

I need to work through this a bit, but it seems like your backreferences idea is closer to what I had originally thought seeing the RFC come in.

With backreferences, instead the proposed:

fn two_args(arg1: &Foo, arg2: &'b Bar) -> &'b Baz

You just write:

fn two_args(arg1: &Foo, arg2: &Bar) -> &'arg2 Baz

To my eyes this solves a couple issues with lifetimes:

  • Just as the proposed solution, I don't have to be confused by generics. Additionally, I can see the relationship up front between the lifetime and where it comes from directly.
  • The relationship reads a little nicer than having stand-in variables
  • Still gives you the "I can skip having to write so many lifetimes" approach

I recognize this might just be the case for this small examples, but I worry though without this things actually get worse.

fn two_args(arg1: &Foo, arg2: &'b Bar) -> &'b Baz

What does 'b come from? It's part of the generics but we've just fabricated the name. It feels a bit.. magical. And may not in the best way.

Contrast to argument-named lifetimes, which I think are less magic. We could just say "a lifetime is bound to the argument name when implied" and that would be that.

For lifetimes that are shared between two arguments, we just reuse the name:

fn two_args(arg1: &Foo, arg2: &'arg1 Bar) -> &'arg1 Baz

Of course, with more advanced stuff, we'd see explicit lifetimes come back, but at least we have a nice complexity slope towards that transition.

@pliniker
Copy link

This resonates strongly. I feel hugely positive about this proposal, including backreferences.

I can't speak to technical details and bikeshedish questions in implementing this rfc but in my use of Rust, my mental model of what lifetime parameters do has just in the past month been challenged and I've found the existing syntax and documentation to be a significant barrier to understanding.

I would recommend all these changes - the simplification of syntax, naming lifetimes after parameter names (I really love the backreferences idea) and the documentation that would read so much more intuitively as a result.

@kennytm
Copy link
Member

kennytm commented Aug 18, 2017

@jonathandturner Backreference breaks down as soon as you have two lifetimes in a type (&&X, (&X, &Y), &T<'x>, T<'x, 'y>, ...). The proposed RFC generalizes a lot better, at a little cost of teaching "any unseen 'x denotes a new lifetime".

// std::cell::Ref::clone as a free function
pub fn clone<'a, 'b>(orig: &'a Ref<'b, T>) -> Ref<'b, T>; // ??

// std::fmt::Arguments::new_v1 as a free function
fn new_v1<'a>(pieces: &'a [&'a str], args: &'a [ArgumentV1<'a>]) -> Arguments<'a>; // ??

// core::fmt::builders::debug_tuple_new
pub fn debug_tuple_new<'a, 'b>(fmt: &'a mut fmt::Formatter<'b>, name: &str) -> DebugTuple<'a, 'b>; // ??

// the compiler is crazy.
pub fn check_loans<'a, 'b, 'c, 'tcx>(
    bccx: &BorrowckCtxt<'a, 'tcx>,
    dfcx_loans: &LoanDataFlow<'b, 'tcx>,
    move_data: &move_data::FlowedMoveData<'c, 'tcx>,
    all_loans: &[Loan<'tcx>],
    body: &hir::Body,
)

@sophiajt
Copy link
Contributor

sophiajt commented Aug 18, 2017

@kennytm - just to be clear I'm not saying only back-references. I'm saying they should be an important part of the design, not an optional part.

@rpjohnst
Copy link

I would prefer to avoid backreferences if possible, because they feel like an inconsistency with the full version that we need anyway. They also make it look like ' is some kind of lifetime-of operator, which is misleading and not a direction that really makes sense given that Rust code never talks about concrete lifetimes.

@ssokolow
Copy link

They also make it look like ' is some kind of lifetime-of operator, which is misleading and not a direction that really makes sense given that Rust code never talks about concrete lifetimes.

You just hit on what what was bothering me that I couldn't quite pin down.

@comex
Copy link

comex commented Aug 18, 2017

Interesting. I'm vaguely positive on this proposal, but for the record, note a few cases where multiple levels of lifetime-declaration nesting could show up in the future, making the case distinction not a slam dunk:

  • Closures, especially if syntax is ever added for generic parameters on closures; but even without that, under the new syntax, people may expect to be able to introduce lifetimes in closure arguments like they can in fn arguments.

  • I'd like it to be possible someday for type declarations within fns (and corresponding impl blocks) to reference generic parameters from the fn; this is possible in other languages, and it can reduce boilerplate in various situations. Thus there could be many nested impl blocks, all of which have lifetime parameters in scope.

@skade
Copy link
Contributor

skade commented Aug 18, 2017

I like this proposal. 👍 I'd prefer if it spelled out the situation around 'b: 'a also in the main sections, it only gets mentioned in the "Drawbacks" section.

I personally think that:

fn foo<T>(&'a self, bar: &'b T) where 'b: 'a, T: 'b {

}

Reads much better then

fn foo<'a, 'b: 'a, T>(&'a self, bar: &'b T) {

}

Because

a) It keeps the type parameter list clear of lifetimes, which always felt a bit odd at this place (especially when using the function through the turbofish...)
b) It has a natural flow: "self has the lifetime 'a, bar borrows with the lifetime 'b, where the relationships are as follows..."

I think where clauses are good practice in anything more complex then one trait bound anyways, so I don't think that's a problem.

Would it be possible to handle this without deprecations? Keep the old method of working valid, but switch towards a lint phrased like "this isn't necessary anymore"? This wouldn't be a deprecation per se, even if we go towards warning on the old style, but more of a nudge towards the better future. I know this feels like I'm just replacing words here, but deprecations always give the feel that something wasn't working right, lints towards improvements clearly frame things as improvements.

@StyMaar
Copy link

StyMaar commented Aug 18, 2017

I'm really excited to see this RFC because I've always found the lifetime syntax to be clunky and verbose.

I'm quite skeptical about the _ marker though, I don't find it really clear and I don't think it adds much, especially if we decide to add backreferences :

// current proposal
fn iter(&self) -> Iter<_, T>

//without the `_` merker
fn iter(&'self self) -> Iter<'self, T>

//with backreferences, not much more verbose than `_` but more explicit
fn iter(&self) -> Iter<'self, T>

@kennytm
Copy link
Member

kennytm commented Aug 18, 2017

👎 on the case-dependency, as other people already mentioned. Otherwise 👍. So no-vote for now.

I hate that I have to write MyStruct<'parent> defining the type, but need to remember to change to MyStruct<'Parent> when implementing a trait for it.

If the purpose of case distinction as a hard requirement is just to tackle the "my screen is too short" problem but not a fundamental typesystem restriction, I'd say just drop it, and instead encourage using more descriptive lifetime names for lifetimes in custom types (e.g. the field's name, 'owner, etc.).


I think '_ is better than _, the latter may be confused for a type parameter (seen as HashMap<_, _> elsewhere).


BTW I suppose the new rules be won't affect closures? Today lifetime elision is not applied to closures.

fn foo<'a>(a: &'a u32, b: &u32) -> &'a u32 {
    let c1 = || -> &'a u32 { a };
    let c2 = |x: &'a u32| -> &'a u32 { x };
    let c3 = |_x: &u32| -> &u32 { b }; // currently these are two different lifetimes
    if *c3(b) < 0 {
        c1()
    } else {
        c2(a)
    }
}

fn main() {}

@strega-nil
Copy link

Can I just say - I love this proposal.

That's all <3

that instead of writing

```rust
fn two_args<'a, 'b>(arg1: &'a Foo, arg2: &'b Bar) -> &'b Baz
Copy link
Member

Choose a reason for hiding this comment

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

Why did you write <'a, 'b> here when the 'a has no reason to be named, but leave the lifetime unnamed on &Foo below?

The way you've currently written it makes the impact of the binding site more significant than it needs to be (which might be great for motivating the RFC, but lets not do it on false pretenses...).

It would be more fair IMO to make the above signature: fn two_args<'b>(arg1: &Foo, arg2: &'b Bar) -> &'b Baz.

Copy link
Member

Choose a reason for hiding this comment

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

Or if you prefer, you could find some other way to incorporate the 'a so that it does have to be named.

For example:

  • Before: fn two_args<'a, 'b: 'a>(arg1: &'a Foo, arg2: &'b Bar) -> &'a Baz<'b>
  • After: fn two_args(arg1: &'a Foo, arg2: &'b Bar) -> &'a Baz<'b>

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch! Will fix in revision.

@djc
Copy link
Contributor

djc commented Aug 18, 2017

I actually like the "lifetimes from types and impls are uppercased", since I think it will help finding the origin of the lifetime bound where they occur.

I would be very much in favor of backreferences that would allow us to write:

fn myfun(&self, foo: &Foo) -> &'self Bar

Since this will actually make it much more attractive to use readable names for lifetimes (since now the "declaration" can be omitted.

The RFC could be clearer on how the different parts of this proposal interact with checkpoints, maybe that could be clarified? Like, "type lifetimes with non-capitalized names will be deprecated in checkpoint 2015, and disallowed in checkpoint 2018".

I don't know the details of elision works today, but would it be an option to adopt this proposal and deprecate elision as it currently is, but then allow unspecified lifetimes in the return type to be omitted if-and-only-if there is a &self in play, as a simpler, easier to predict and remember version of elision that only works on methods?

@withoutboats
Copy link
Contributor

I think we should implement this, use it on nightly, and see what conventions or lints make sense arrising from that experience. This conversation about how to distinguish function level from impl level lifetimes seems to me like a prime example of how our processes have become far too "waterfall" - its one thing to be aware of this concern, and know we may need a way to mitigate it, but its another thing to have a lengthy debate about what the best mitigation strategy is without getting any practical experience of the situation.

@burdges
Copy link

burdges commented Sep 9, 2017

We could permit ambiguity only on multi-letter lifetimes, under the assumption that their name corresponds to come meaningful convention in the code base, but require that single letter implicitly bound lifetimes were upper or lower case corresponding to whether they were implicitly bound by the impl or fn.

@TimNN
Copy link
Contributor

TimNN commented Sep 9, 2017

Since the topic of experimenting has been brought up, has anyone considered making this an experimental RFC? The only thing that would change is requiring another RFC before stabilising the feature, which seems reasonable given that there are still concerns, which I don't think can really be resolved without some experimentation.

@vitiral
Copy link

vitiral commented Sep 9, 2017

I would just like to point out that the whole problem here is that this RFC is essentially auto declaring variable names (for a special kind of variable: the lifetime).

Until this RFC, you had to declare your lifetime and their "scope" with the fn foo<'a, 'b, ...>(a: &'a [u8]...) syntax. This is trying to remove that boilerplate by auto-inferring the names. Unfortunately, this makes the scope of lifetimes no longer clear.

It's really rather annoying. It feels like this problem should be solvable. I can think of a few possible solutions, although I like none of them:

  • lifetimes attached to structs must be capitalized (and must not be capitalized for methods) for auto-inferring to kick in. This was already suggested
  • add some kind of sigil to (i.e. @) to denote that the lifetime is declared elsewhere. 'foo is always local, but '@foo is always defined in the impl header. I am pretty opposed to adding new sigils
  • lint to require 'self::<lt> for non-local lifetimes. This is at least extremely clear.
impl<'abc> for MyStruct<'abc> {
    fn foo() -> &'self::abc [u8] {}
}

@rfcbot
Copy link
Collaborator

rfcbot commented Sep 10, 2017

The final comment period is now complete.

@nikomatsakis
Copy link
Contributor

@glaebhoerl

We could allow uppercase lifetimes on free-standing functions, I think -- there's no ambiguity or potential for conflict there. Just not on methods inside of impl blocks. (edit: so to be clear, you could still have globally consistent names (IINM), they'd just be the uppercase ones)

I considered this. I held back on suggesting it because the "globally consistent names" in some cases would have to be lower-cases:

impl MyType { // no lifetime parameters
    fn process(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { .. } // can't be `'Tcx` here
}

I feel like in practice I would prefer a lint that says "names used across scopes must have more than one letter", but mostly I agree with @withoutboats that we should work this out after gaining some more experience "live":

[@withoutboats]: This conversation about how to distinguish function level from impl level lifetimes seems to me like a prime example of how our processes have become far too "waterfall"

Indeed.

--

@TimNN

Since the topic of experimenting has been brought up, has anyone considered making this an experimental RFC?

I'm not opposed, but it seems unnecessary to me personally. I feel like we could leave an official "Unresolved Question" of "what naming convention would be best to distinguish the scopes", so that we are sure to revisit the question prior to stabilizing.

That is, to me, an eRFC is needed when there are major pieces of the design missing. For example, in the generators RFC, it was unclear what syntax we should use, whether a special trait (Generator) was needed or whether we could leverage FnMut, etc. Here, it feels like we know we want a lint, we just don't know precisely what it should do.

(Though, to be honest, I think I'd like to merge the RFC + eRFC process.)

@aturon
Copy link
Member Author

aturon commented Sep 12, 2017

Thanks, all, for the thorough discussion! This RFC has now been merged! Tracking issue

During FCP, most discussion centered on concerns about the right convention (if any) to lint enforce for avoiding accidental clashes between impl headers and the items they contain. As @withoutboats said, however, it seems best to settle this particular question with some experience with the feature in hand; it's difficult to judge hypothetically. This has been added as an explicit unresolved question on the tracking issue, which must be revisited prior to any stabilization.

@aturon aturon merged commit 1be52cd into rust-lang:master Sep 12, 2017
@vitiral
Copy link

vitiral commented Sep 12, 2017

no one commented on my proposal for 'self::lifetime_name ☹️

@burdges
Copy link

burdges commented Sep 12, 2017

You mean when referred to from outside the same declaration? Yes I think 'self::lifetime_name would work for the impl, along with 'fn::lifetime_name for the fn or maybe 'fn_name::lifetime_name. I do think that syntax is unambiguous.

@vitiral
Copy link

vitiral commented Sep 13, 2017

@burdges I don't think 'fn:: is necessary, since there are no global lifetimes except for 'static. A function declares all it's lifetimes along with the variable names (under this RFC)

@nox
Copy link
Contributor

nox commented Sep 13, 2017

Once experimentation started for a while, how do we quantify whether these changes aren't too dangerous to actually become a thing?

@dhardy
Copy link
Contributor

dhardy commented Sep 13, 2017

Is this bit of the RFC wrong (outdated)? It mentions further down that the "backreference" syntax is not preferred.

fn elided(&self) -> &str
fn two_args(arg1: &Foo, arg2: &Bar) -> &'arg2 Baz
fn two_lifetimes(arg1: &Foo, arg2: &Bar) -> &'arg1 Quux<'arg2>

@vitiral
Copy link

vitiral commented Sep 13, 2017

@aturon the rendered link is now broken

@bmisiak
Copy link

bmisiak commented Apr 16, 2018

As a Rust novice, I can attest the guide proposed in this RFC made lifetimes annotation in structs 'click' for me. 👍

@rpjohnst rpjohnst mentioned this pull request May 5, 2018
jturner314 added a commit to jturner314/rust-rfcs that referenced this pull request Jun 23, 2018
Backreferences are listed as a "possible extension or alternative" in
RFC rust-lang#2115, so the examples should not include them. This is further
reinforced by commit c20ea6d, which
appears to have been intended to remove all instances of
backreferences in the examples, but missed these. [@dhardy also
observed this
issue](rust-lang#2115 (comment)).
jturner314 added a commit to jturner314/rust-rfcs that referenced this pull request Jun 23, 2018
Backreferences are listed as a "possible extension or alternative" in
RFC rust-lang#2115, so the examples should not include them. This is further
reinforced by commit c20ea6d, which
appears to have been intended to remove all instances of
backreferences in the examples, but missed these. [@dhardy also
observed this issue]
(rust-lang#2115 (comment)).
jturner314 added a commit to jturner314/rust-rfcs that referenced this pull request Jun 23, 2018
Backreferences are listed as a "possible extension or alternative" in
RFC rust-lang#2115, so the examples should not include them. This is further
reinforced by commit c20ea6d, which
appears to have been intended to remove all instances of
backreferences in the examples, but missed these.
@warlord500
Copy link

warlord500 commented Jun 27, 2018

I would definitely not want the part where you don't have to declare lifetimes because of added inconsistency in the language and ambiguity of where life time parameters come from.

I also dont like back reference lifetime, it adds a really weird special case to lifetime.
making lifetimes even more confusing because of the rules of describing bounds on them.
where the concept behind them is rather simple

@Centril Centril added A-typesystem Type system related proposals & ideas A-lifetimes Lifetime related proposals. labels Nov 23, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lifetimes Lifetime related proposals. A-typesystem Type system related proposals & ideas Ergonomics Initiative Part of the ergonomics initiative final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. 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.

None yet