I am very exited about this discussion. Lifetimes based on lexical scopes were a major frustration for me and ultimately the main reason why I didn't adopt Rust for my current project even though I really like the language. Specifically, I am looking to solve a following puzzle. I have a non-trivial data structure (in following DS) that uses custom indices to manipulate it. The index is not a machine pointer, but can be a fairly intricate combination of references to the internal state of the DS (e.g. offsets into internal tables + state flags etc.). Furthermore, indices potentially become invalid once the DS is mutated. Therefore, in order to have a safe implementation of the DS, we'd ideally want to throw compile-time errors if any of the indices are alive when the host structure is mutated.
I attempted to achieve this in Rust using lifetime parameters. When an index is created, it is bound to the lifetime of the DS, resulting in implicit immutable borrow (no actual reference is created, which is again desirable). While this ensures that I can't mutate the DS before all the indices go out of scope, it also makes the indices fairly useless as there is no apparent way to consume them. Specifically, I'd like to do something like this:
where the signature of the methods is something like:
fn consume<'a, 'b: 'a>(&'b mut self, index0: Index<'a>)
fn join<'a, 'b, 'c: 'a+'b>(&'c mut self, index0: Index<'a>, index1: Index<'b>) // how do you even state that
// 'c outlives both 'a and 'b?
The intuition here is that while indices do borrow the collection, they can't outlive the method call (as the index is moved). Furthermore within the mutation method, they get destructured into the implementation-dependent internal state before the collection is actually mutated. Therefore, I would consider this pattern "safe" — there is no way that the mutable borrow of self will interfere with the implicit immutable borrow within the index. In another words, since the collection technically owns the index (the later cannot exist outside the particular collection state), it is safe for the collection to consume it. However, I don't see any way of implementing this in current Rust — the compiler of course complains about the mutable borrow of self while there is still an outstanding implicit immutable borrow by the index.
Now, I have to admit that I haven't fully understood the approach to NLL suggested by Nicholas in his post. I'll certainly spend some time looking at it, but just to satisfy my curiosity: will this approach allow one to implement patterns like the above?