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 <[email protected]>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
>
> fn select<'T>(shape: &'Shape, threshold: float,
>              a: &'T, b: &'T) -> &'T {
>     if compute_area(shape) > threshold {a} else {b}
> }
>
> (not very useful case)
>
> ---
>
> fn select<'r, T>(shape: &Shape, threshold: float,
>                  a: &'r T, b: &'r T) -> &'r T {
>     if compute_area(shape) > threshold {a} else {b}
> }
>
> becomes
>
> fn select<T>(shape: &Shape, threshold: float,
>              a: &T, b: &T) -> &T {
>     if compute_area(shape) > threshold {a} else {b}
> }
>
>
> --------------------------------------------------------------------------------
>
> Other examples:
>
> ---
>
>      struct Iterator<'lt, T> {
>          source: &'lt [T],
>          index: uint
>      }
>
>      fn has_next<'a, 'b, T>(iter: &'a Iterator<'b, T>) -> bool {
>          iter.index + 1 < iter.source.len()
>      }
>
>      fn next<'a, 'b, T>(iter: &'a mut Iterator<'b, T>) -> Option<&'b T> {
>          iter.index += 1;
>          if iter.index < iter.source.len() {
>              Some(&iter.source[iter.index])
>          } else {
>              None
>          }
>      }
>
> becomes:
>
>      struct Iterator<'T> {
>          source: &'[T],
>          index: uint
>      }
>
>      fn has_next<T>(iter: &Iterator<T>) -> bool {
>          iter.index + 1 < iter.source.len()
>      }
>
>      fn next<T>(iter: &mut Iterator<T>) -> Option<&T> {
>          iter.index += 1;
>          if iter.index < iter.source.len() {
>              Some(&iter.source[iter.index])
>          } else {
>              None
>          }
>      }
>
> ---
>
>      impl<'b, T> Iterator<'b, T> {
>          fn has_next<T>(&self) { /* same as before */ }
>          fn next<T>(&mut self) -> Option<&'b T> { /* same as before */ }
>      }
>
> becomes
>
>      impl<T> Iterator<T> {
>          fn has_next<T>(&self) { /* same as before */ }
>          fn next<T>(&mut self) -> Option<&T> { /* same as before */ }
>      }
>
> ---
>
> Regards,
> Paulo
>
>
>
>
>
>
> _______________________________________________
> Rust-dev mailing list
> [email protected]
> https://mail.mozilla.org/listinfo/rust-dev
>
>
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to