struct LinkedList {
val: u64,
next: Box<LinkedList>,
}
impl LinkedList {
fn get_index(&mut self, idx: usize) -> u64 {
let mut node = self;
for i in 0..idx {
node = &mut node.next;
}
node.val
}
}
fn main() {}
There are a number of errors, but this is basically what it complains about:
<anon>:10:13: 10:34 error: cannot assign to `node` because it is borrowed [E0506]
<anon>:10 node = &mut node.next;
arielb1 notes in the issue that you can fix this by changing &mut node.next to &mut {node}.next and says this is about reborrow vs move, linking to https://www.reddit.com/r/rust/comments/46rii1/when_to_borrow_move_or_copy/. Unfortunately I'm not finding that link very illuminating, so I figured I'd post here rather than polluting the issue.
In short: what additional information does block brackets around node give the compiler to allow this to work? I'm aware of the usage of blocks to restrict borrows, but I always see that with an expression or multiple statements rather than a single variable name - intuitively, I'd expect {x} to be the same as x. Any help much appreciated!
Basically, in Rust, there are a few places where you could either re-borrow or move a particular object. The default that the compiler chooses, of reborrowing, generally works out well. But in some cases, that default isn't actually correct.
When you pass an object into a function or method by value, that moves it into the function, and if you return it it moves it out. A block acts similarly; if you return a value from a block by writing it as the last expression in the block, the value is moved out.
Now, in this case, node is an &mut LinkedList. The default when writing &mut node.next is to reborrow node, so you can still use it later on once the reborrow ends. However, in this case, that causes a problem as you are trying to actually update node, but it's reborrowed by the &mut node.next expression. If you instead wrap node in a block, so the block is returning the value of node, it has to be moved out of instead of reborrowed; and that actually does what you want here, since you are actually just discarding the original reference to node when traversing to the next one.
That's a strange way of using Rust's features and also hacking around the limitation. &mut {node} this blew my mind, didn't think of this before that...
When I see { something_here } I instantly think of initialization(because of C++).
Interesting, because I would of thought of:
node = {
node
}
But because of C++, my mind blacks out on node = {node}, thinking of initialization.
I still don't understand why even with {node} it works. &mut self is a mutable reference. let mut node = selfshould also be a mutable reference(you don't take ownership let mut node = *self)...
Should it not allow you to move out node?
I would expect {node} to throw an error saying that you cannot move it out because it is a reference, you are not the owner of the data. In my mind this moves out self, which I guess I got it wrong.
{node} is not moving the thing node points to, it's moving the pointer itself. &mut T pointers are non-copyable, meaning they can only be moved or re-borrowed.
If Rust didn't do automatic re-borrowing, then any time you did func(some_mut_borrow), then some_mut_borrow would be unusable after the function call. That would be a huge pain in the backside, so the compiler inserts the rough equivalent of func(&mut *some_mut_borrow) for you.
Thanks to everyone for the explanations and links. I realise now that my problem was that I didn't really understand reborrowing or why it's used. I do now!
Sorry for bumping, but the link went dead (archive) and this thread is easier to find than up-to-date documentation. I think the NLL RFC covers liveness and reborrowing now.