|
|
|
|
|
by cpeterso
3414 days ago
|
|
As a C++ programmer, I understand ownership and borrowing, but I find the lifetime syntax impenetrable. It clutters the code with annotations that seem like they could be deduced by the compiler. For example, a variable's lifetime is relative to another variable or function. Why must we create a placeholder name like 'a instead of referring to the other variable by name? Take this example from section 10.3: fn longest<'a>(x: &'a str, y: &str) -> &'a str {
x
}
Why couldn't we write it as something like the following? fn longest(x: &str, y: &str) -> &'x str {
x
}
Or instead of requiring lifetimes everywhere, the compiler could use conservative defaults but allow optional lifetime annotations to reduce the lifetime if needed or to release memory sooner. For example, in this struct from section 10.3, why is 'a necessary when config is a non-mut reference and thus must outlive a struct App instance? There is a Rust issue filed for this: https://github.com/rust-lang/rfcs/issues/1532 struct App<'a> {
name: String,
config: &'a Config,
}
|
|
The reason why the former is not done is because it makes the signature dependent on the body. This means that changing some code could potentially change the signature of the function, which is the kind of silent magic that folks would prefer to be explicit. Also, as the function gets larger, this is harder to deduce by looking at the code. The current elision rules all operate on the signature, so you only need to look at the signature to figure out the lifetimes, elision or not.
The latter is way more controversial. The reason there is similar; folks want it to be obvious from the type name that it is a borrowing type, and changes to the internals shouldn't magically change the signature without an explicit acknowledgement.
Even type arg lifetime elision was controversial. Currently, in Rust, you can write a function like `fn foo(x: &u8) -> Foo`, where `Foo` is actually `Foo<'a>`, and the compiler inserts the lifetimes in the right places. When this was proposed there was a good chance that it would not happen (though as you can see it did happen). So the status quo on lifetime elision is probably not going to change in my opinion, given how hard it was to make the last one happen (of course, you have the "overton window" of acceptable implicitness gradually shifting due to that change, so it might after all)
To me personally, this explicitness is a minor annoyance when dealing with simpler stuff, but is invaluable in codebases making heavier use of lifetimes, like rustc (which avoids reference counting, even though compilers generally need a lot of tricky sharing). And ultimately, I appreciate it even in codebases with fewer lifetimes; I don't like having to peek at a function's code to understand how it's supposed to be used.