4 ms·
I'm not quite sure how non-lexical lifetimes is supposed to fix this issue. Control-flow analysis doesn't change the fact that the arguments are evaluated in le
by eridius 11y ago
I'm not quite sure how non-lexical lifetimes is supposed to fix this issue. Control-flow analysis doesn't change the fact that the arguments are evaluated in left-to-right order, and it doesn't change the fact that the &mut reference is created prior to the method arguments being evaluated and lives until the method itself is invoked. Control flow analysis will simply confirm the fact that the &mut borrow exists while the arguments are evaluated.
What might fix this is a special-case rule where an implicit¹ & or &mut reference as a method receiver is temporarily ignored while the arguments are being evaluated (with the borrow checker double-checking after the arguments are evaluated that the & or &mut reference is still legal, i.e. that the referenced value wasn't moved and, in the case of a &mut reference, that no other borrow exists as a result of evaluating the arguments). This rule, being a special case, doesn't require non-lexical lifetimes.
¹I say implicit because it would be potentially confusing for an explicit reference to be ignored while the arguments are evaluated.
- webkike 11y agoMy guess is that if a mutable reference dominates the lifetimes of all its borrows, then an exception to the rule can be made.
- kibwen 11y agoYou're correct in that I was painting with too broad a stroke by attributing all of NLL to control-flow analysis, I was just trying to be brief. :) Having knowledge of control flow is just one of the approaches that will be used to achieve non-lexical lifetimes, though it is particularly applicable when also talking about MIR given that MIR is structured as a control-flow graph.
- cwzwarich 11y agoThe assumed fix here is to distinguish between a mutable borrow and the reservation of a location to later do a mutable borrow. Locations based on local variables and their fields can be reserved, although a move out of the variable would invalidate a reservation. Other locations (e.g. the result of a function call) can't be reserved and would give an error.
- eridius 11y agoSeems like it would be confusing to allow let mut nums = Vec::new(); nums.push(nums.len()); but disallow let mut nums = Box::new(Vec::new()); nums.push(nums.len()); The second example would have to be disallowed because the mutable reference is created by calling DerefMut::deref_mut().
- cwzwarich 11y agoThe particular case of Box is safe, and is along the lines of special knowledge about Box that is assumed by the type checker, but the special-cased nature of Box has been gradually decreasing over time. I don't think there would be an easy safe way to allow user-defined types to establish same guarantee. In retrospect, changing the evaluation order of a function with respect to its arguments might have been a better choice, but something like the solution I mentioned is probably the only backwards-compatible option.