Most of the time that I've seen return value lifetimes specified, they
aren't returning something of the same type as a parameter. For
instance, the vec module has "fn head<'r, T>(v: &'r [T]) -> &'r T". How
common is it for functions to actually return something of the same
type, as opposed to a type that the parameter type contains?

-doy

On Tue, Apr 23, 2013 at 07:17:16PM +0100, 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 <[email protected]
> ><mailto:[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] <mailto:[email protected]>
> >    https://mail.mozilla.org/listinfo/rust-dev
> >
> >
> 
> _______________________________________________
> 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