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