We can do the same kind of (basic?) borrow checking with those can't we ?
We can do the same kind of (basic?) borrow checking with those can't we ?
It's important to note that lifetimes are not just for the compiler. They are also documentation / API contracts:
fn add_to_vec<'a>(element: &'a str, vec: &mut Vec<&'a str>) { ... }
This documents that the vec passed into the function can't outlive the element that's passed in. That's because the string (a reference (pointer) to the string data) that's passed in gets stored in the vec. So freeing the string and then using the vec would result in a use after free.
You can't have this power without a syntax for it. You could of course do some global analysis, but now you can't have API contracts like this anymore, which are important for semantic versioning.
An alternative syntax a language could use would be to have arbitrary pre- and post-conditions that the compiler can verify, which would be a whole lot more powerful, but also a lot more wordy than the short lifetime "tags" that Rust uses.
fn get_reference_to_first_element(x: &[u8]) -> &u8 {
return &x[0]
}
There are lifetimes here that express that the reference that is returned from this function will live as long as the reference that was passed in. In cases like this where the association is obvious, the lifetimes are implied and you don't need to write them. The desugared form looks like this: fn get_reference_to_first_element<'a>(x: &'a [u8]) -> &'a u8 {
return &x[0]
}
Lifetimes are present on structs for the same reason.A system that is limited to annotating only function input parameters would be limited to inferring the programmer's intent everywhere, which limits its utility to simple cases (which might be fine for some languages, but I suspect will not satisfy C++).
In addition, from glancing briefly at that video, it seems as though those five keywords seem to be more concerned with ensuring use-after-initialization, whereas Rust's lifetime system is about preventing use-after-free.