The unmatched single quotes are extremely unnatural for people coming from a C/C++ background (our primary target audience). I’ve heard this complaint from some of my smarter and more knowledgeable friends (who are also familiar with Haskell and other languages, and not just living in a C-family cocoon). For that matter, I share it myself. The rest of Rust’s syntax does a really fantastic job of fitting new concepts into a familiar C-style syntax in an intuitive way; it’s only 'lifetimes
which stick out like a sore thumb and break the metaphor.
I don’t really agree with the “it’s good that the syntax is weird, because lifetimes are weird” argument. If we go out of our way to make something seem intimidating, that does not make it easier to learn. This is kind of similar to the "unsafe
code should be verbose and scary, to discourage people from writing it" thing, and how people tend to perceive “monads” as an alien concept from outer space because of the way they’re usually presented.
I do agree very much that the syntax should be clear about what it means, as far as possible, and not try to sweep things under the rug. But that doesn’t mean it necessarily has to be strange or scary.
My preference would be to drop the current ticks everywhere a lifetime is mentioned, and instead use an explicit lifetime
or scope
keyword (whichever way that decision goes) where a lifetime variable is introduced. So where we currently write:
fn get_bar<'a>(foo: &'a Foo) -> &'a Bar;
that would instead be:
fn get_bar<lifetime a>(foo: &a Foo) -> &a Bar;
or:
fn get_bar<scope a>(foo: &a Foo) -> &a Bar;
Manually written lifetimes are going to be infrequent due to elision, so it doesn’t hurt to be explicit (and likely helps for comprehension). For user-defined types with lifetimes it would go likewise, e.g.:
fn my_get_bar<lifetime a>(foo: MyRef<a, Foo>) -> MyRef<a, Bar>;
We could also tweak the syntax of references to use {}
, which would be a bit noiser but also perhaps more obvious and suggestive ("this reference is valid in the block a
"):
fn get_bar<scope a>(foo: &{a} Foo) -> &{a} Bar;
fn get_bar_mut<lifetime a>(foo: &mut{a} Foo) -> &mut{a} Bar;
This possibility was floated back when lifetimes were changed from the previous /
syntax to the current '
; many people preferred it, but it was blocked by a syntax ambiguity. I’m not sure whether we would still have that issue. (It might’ve been something to do with struct
literals, whose grammar has seen some improvements since then.)
(And in case it’s not obvious, I was alternating between scope
and lifetime
in the above only because I didn’t want to want to write everything twice; we would use whichever word we decide on.)