[rust-dev] Simplifying lifetime expression

Steven Blenkinsop steven099 at gmail.com
Tue Apr 23 11:59:26 PDT 2013

A fresh lifetime is a better default behaviour.  For example,
    fn F(x: &mut T)
could be either
    fn A<'a>(x: &'a mut T<'a>)
    fn B<'a, 'b>(x: &'a mut T<'b>)

In A, it's possible for any borrow with lifetime 'a to escape via the
mutable state in x, including the borrow into x itself. This makes it so
you can't call A with any `&mut T` that has a shorter lifetime than the
contents of the T, and the borrow into A will be alive and inaccessible for
the remainder of the lifetime of the contents of the T. B is almost
certainly what people will want when they write F.

On Tuesday, April 23, 2013, Paulo Sérgio Almeida wrote:

> Ok. Suppose the current syntax is frozen. But I talked about two things:
> syntax for explicit lifetimes (mainly point 3, altough a bit also in points
> 1 and 2) and defaults for when no explicit (named) lifetimes are used.
> Even if no syntax change is made, for the cases when named lifetimes are
> used, what I find specially painful is the default rule when no explicit
> lifetimes are used:
> "an implicit fresh lifetime for each parameter and result".
> This rule, specially when a result is involved, which is exactly one of
> the cases when stating lifetime is needed, seems to make little sense, and
> will force named lifetimes to be used in many cases, causing noise and
> distraction and decreasing legibility.
> In what way is the default that I propose, which basically is:
> "one implicit fresh lifetime for each type parameter and another for all
> the remainder parameter types"
> confusing or a possible source of obscurity? I find it quite natural that,
> e.g., if I am returning a borrowed reference (not a copy, not an owned, not
> a @) of type T and I have received one or more borrowed references to T,
> i.e.:
> fn f<T>(p1: &T, p2: &T, ...) -> &T  { ... }
> the *natural* thing to expect is that the result comes from one of the T
> that I received as parameters, and the default that I propose is not
> obscure at all, but makes the code cristal clear. I find having to write
> fn f<'r, T>(p1: &'r T, p2: &'r T, ...) -> &'r T  { ... }
> because of the current defaults, needlessly painful and distracting. Is
> the current default less obscure? Will it cause less surprise? Or it is the
> other way around ...
> Regards,
> Paulo
> On 4/23/13 4:11 PM, Niko Matsakis wrote:
>> Thanks for your proposal. We've been through a number of rounds with the
>> lifetime syntax, including an earlier scheme that was quite similar to
>> what you propose in some regards (there was a single anonymous lifetime
>> parameter that was used whenever you wrote `&T`, which meant that yes
>> you often did not need to use explicit lifetime parameter names).
>> However, we found that this was ultimately more confusing to people than
>> helpful. One of the goals of the current syntax was to make the
>> mechanism of named lifetimes more explicit and thus more clear to the
>> reader. Smarter defaults have the disadvantage that they often obscure
>> the underlying system, making it harder to learn what's really going on.
>> In practice,named lifetimes don't seem to be that common, at least for
>> me. We should do some numerical analysis, but I've found subjectively
>> that most functions simply consume data and do not return pointers. This
>> is particularly true for "non-library" code, and even more true with
>> judicious use of `@` pointers (it is often not worth the trouble to
>> avoid the GC; it can make life much easier, that's what it's there for).
>> So in summary I do not think it likely we will change the current syntax.
>> Niko
>> On Mon, Apr 22, 2013 at 12:21 PM, Paulo Sérgio Almeida <psa at di.uminho.pt
>> <mailto:psa at di.uminho.pt>> wrote:
>>     Hi all,
>>     (My first post here.)
>>     First, congrats for what you are trying to achieve with Rust. I
>>     arrived very late to the party, and so I am not sure that what I
>>     will say can be of use. But as I understand, lifetimes is one of
>>     those things that are not completely solidified, and anyway, better
>>     late than never.
>>     Looking at some code, expressing lifetimes of borrowed references is
>>     one of those things that is somewhat bewildering, making the code
>>     somewhat noisy. I think it can be improved through a better choice
>>     of defaults and a slight change in explicit lifetimes.
>>     The main thing I think is "wrong" is the defaults for function
>>     parameters. From the tutorial: "the compiler simply creates a fresh
>>     name for the lifetime automatically: that is, the lifetime name is
>>     guaranteed to refer to a distinct lifetime from the lifetimes of all
>>     other parameters."
>>     This default is not very useful. For example, it is wrong basically
>>     everytime we want to return a borrowed pointer (unless, for a global
>>     variable?). The more common case is returning something with the
>>     same lifetime as some parameter. In many cases we don't need to
>>     distinguish parameters, and specify which we are returning, in
>>     others we want to split parameters in two equivalence classes, the
>>     one from which we are returning, and everything else.
>>     When type parameters are involved, a return type typically says
>>     where the result comes from most of the times. Again, the default
>>     should be different. E.g., when we are returning a borrowed
>>     reference to a "key" of type parameter K, the default should be
>>     "other things also of type K in parameters".
>>     Finally, with better defaults, in the remaining cases where we need
>>     to explicitly express lifetimes, having to "invent" identifiers is a
>>     nuisance. Also the space which must be used between the lifetime
>>     identifier and the type is too distracting and makes it cumbersome
>>     (for humans) to "parse" the type of some identifier.
>>     I have been thinking about this and have the following proposal. (Of
>>     course there may be inumerous things that must have escaped me, but
>>     here it goes anyway.)
>>     ---
>>     Regarding types for borrowed references in function parameters or
>>     result, and type parameters:
>>     1) each type-parameter has an associated implicit lifetime, which by
>>     default is different from the lifetimes of other type-parameters or
>>     normal function parameter types, but it can be qualified in each
>>     declaration or use with an explicit lifetime;
>>     2) function parameter types or return type that are not type
>>     parameters have all the same implicit lifetime by default, but they
>>     can be qualified explicitly with some lifetime.
>>     3) explicit lifetimes are written identifier' instead of
>>     'identifier; a null identifier is allowed, as in &'T, to qualify a
>>     reference &T with lifetime '.
>>     ---
>>     (Another useful possibility would be allowing several ' at the end,
>>     allowing e.g., &T, &'T, &''T as borrowed references to the same type
>>     but with different lifetimes. In practice, a single ' plus 1) and 2)
>>     will cover "99%" of cases. We could even get rid of using
>>     identifiers, using only several '. But this is not relevant for the
>>     main proposal.)
>>     1) and 2) are about defaults for implicit lifetimes, which currently
>>     are "fresh lifetime for each parameter", and 3) is about simplifying
>>     the remaining cases of explicit expression. The motivation for 3) is
>>     to remove both the need of a space separating lifetime from type and
>>     also the need to "invent" lifetime identifiers. Rewriting examples
>>     from the tutorial and elsewhere, under this proposal, would make
>>     code more legible and standard, reducing a lot the need for explicit
>>     lifetime expression. Things would "just work" as wanted most times.
>>     ------------------------------**------------------------------**
>> --------------------
>>      From the Rust Borrowed Pointers Tutorial
>>     ---
>>     fn get_x<'r>(p: &'r Point) -> &'r float { &p.x }
>>     becomes
>>     fn get_x(p: &Point) -> &float { &p.x }
>>     ---
>>     fn select<'r, T>(shape: &'r Shape, threshold: float,
>>                       a: &'r T, b: &'r T) -> &'r T {
>>          if compute_area(shape) > threshold {a} else {b}
>>     }
>>     becomes
>>         Rust-dev at mozilla.org <mailto:Rust-dev at mozilla.org>
>>     https://mail.mozilla.org/**listinfo/rust-dev<https://mail.mozilla.org/listinfo/rust-dev>
> ______________________________**_________________
> Rust-dev mailing list
> Rust-dev at mozilla.org
> https://mail.mozilla.org/**listinfo/rust-dev<https://mail.mozilla.org/listinfo/rust-dev>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/rust-dev/attachments/20130423/a61a4555/attachment.html>

More information about the Rust-dev mailing list