We are reaching a degree of consensus on the new lifetime syntax.
The general idea is as follows.
# Lifetime names and borrowed pointers
First, `'lt` is the new lexical class for lifetime names. A borrowed
pointer has the (fully specified) form `&'lt mq Type`, where `'lt` is
the lifetime and `mq` is the mutability (for slices, the syntax is `&'lt
mq [T]` and `&'lt str`). As today, `mq` can be `mut` for mutable or
nothing for immutable (I believe `const` for read-only is deprecated and
hopefully going away). So some examples:
&'lt int
&'lt mut int
In some cases, the lifetime may be omitted:
- In a function declaration: `&int` is short for a fresh lifetime name
(meaning a different name from all the other names in scope)
- In a function body: `&int` is short for an inferred lifetime
# Lifetime parameters and fn/struct declarations
When you define a function or type, it can be lifetime parameterized.
Lifetime parameters are declared within `<>` just like type parameters.
If there are both lifetime and type parameters, lifetime parameters must
appear first. For example, here is a simple iterator type that iterates
over a slice. I have written all lifetime parameters in full, later we
will see that not all of them are necessary. (This example compiles
today, albeit with different syntax)
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
}
}
When defining a type, all lifetime parameters must be specified in full
(as shown). When defining a function, lifetime parameters may be
omitted if you simply with to use a fresh lifetime. The fn
`has_next()`, for example, can be simplified to:
fn has_next<T>(iter: &Iterator<T>) -> bool { /* same as before */ }
This is exactly equivalent. Note that we omitted both the lifetime `'a`
that defined the lifetime of the pointer `iter` and the lifetime `'b`
which defined the lifetime of `iter.source`. Because this is part of a
fn signature, the default for omitted lifetimes is to substitute fresh
lifetimes. If we had written the type `&Iterator<T>` inside the fn
body, the default would be to infer appropriate lifetimes. Anywhere
else where we might write that type, for example as part of a struct or
type declaration, there are no defaults and everything must be specified
in full.
The fn `next()` can be simplified as well, but not *quite* as much:
fn next<'b, T>(iter: &mut Iterator<'b, T>) -> Option<&'b T> { /*
same as before */ }
Here the named lifetime parameter `'b` must remain. This is because it
is needed to link the lifetime parameter of the input iterator to the
returned option. Note that the lifetime of the pointer `iter` itself is
not important: the only important thing is the lifetime of the vector
`iter.source`, which is `'b`.
# Lifetime parameters andimpls
This is what we were discussing in the method but we were cut off. If
we wanted to convert the two iterator functions to an impl, we could so
as follows (again, we begin with the *fully explicit* form):
impl<'b, T> Iterator<'b, T> {
fn has_next<'a, T>(&'a self) { /* same as before */ }
fn next<'a, T>(&'a mut self) -> Option<&'b T> { /* same as
before */ }
}
As before, lifetimes can be omitted in method declarations if they are
not used anywhere else, so this can be simplified to:
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 */ }
}
This is *slightly* different than today. This is because, today,
`&self` is equivalent to a self type of `&'b Iterator<'b, T>` rather
than using a fresh lifetime (like `&'a Iterator<'a, T>`). The current
system causes various issues in real-life examples, including this
specific iterator example.
# Multiple lifetime parameters
Note that this proposal easily permits having multiple lifetime
parameters on a given struct. One unclear point is whether you should
be able to specify only one of many lifetime parameters. I am inclined
to say no, it's all or nothing. For example, given this struct:
struct Foo<'a, 'b>
You can write `Foo` or `Foo<'lt1, 'lt2>` but not `Foo<'lt1>`.
# Labeling blocks and expressions
Looking to the future, I personally would like to, further out, support
lifetime names as labels on blocks or expressions, so that you could
write something like:
'a: { let x: &'a T = ...; }
Here `'a` would be a name for the block and it could be used as a
lifetime in type annotations and so forth within the method. This would
help in allowing users to write explicit annotations. It also helps for
breaking/looping in named loops etc:
'a: while cond1 {
'b: while cond2 {
...
break 'a;
}
}
Niko
_______________________________________________
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev