Following code gives error if cnode isn't manually dropped after each iteration. As it's going out of scope, it should be automatically dropped, and I think there is no chance of outlive the borrowed value. But it complains with error borrowed value does not live long enough.
#[cfg(feature = "localrun")]
#[derive(Debug, PartialEq, Eq)]
pub struct TreeNode {
pub val: i32,
pub left: Option<Rc<RefCell<TreeNode>>>,
pub right: Option<Rc<RefCell<TreeNode>>>,
}
#[cfg(feature = "localrun")]
impl TreeNode {
#[inline]
pub fn new(val: i32) -> Self {
TreeNode {
val,
left: None,
right: None
}
}
}
// use std::borrow::Borrow;
use std::rc::Rc;
use std::cell::RefCell;
impl Solution {
pub fn max_depth(root: Option<Rc<RefCell<TreeNode>>>) -> i32 {
let mut depth = 0;
let mut vec = vec![];
if let Some(vnode) = &root {
vec.push(vnode.clone());
}
while !vec.is_empty() {
let mut size = vec.len();
while size > 0 {
size -= 1;
let cnode = vec.pop().unwrap();
if let Some(lnode) = cnode.borrow().left.as_ref() {
vec.push(lnode.clone());
}
if let Some(rnode) = cnode.borrow().right.as_ref() {
vec.push(rnode.clone());
}
// drop(cnode); // <---- Uncommenting This fixes the error though
}
}
unimplemented!()
}
}
#[cfg(feature = "localrun")]
struct Solution{}
Edit: Full error msg:
error[E0597]: `cnode` does not live long enough
--> src/w35_w36_easy/n104.rs:105:30
|
105 | if let Some(rnode) = cnode.borrow().right.as_ref() {
| ^^^^^^^^^^^^^^
| |
| borrowed value does not live long enough
| a temporary with access to the borrow is created here ...
...
109 | }
| -
| |
| `cnode` dropped here while still borrowed
| ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Ref<'_, TreeNode>`
|
help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
|
107 | };
| +
I just noticed the helpful error message from compiler. It says to add a semicolon after if block. Actually it fixed the error! Though I'm not sure why semicolon made the difference.
This is actually quite interesting, but requires delving a little more deeply than you might expect.
A while loop is, like most things in Rust, an expression—which is to say that the loop itself evaluates to some value (currently it's always of unit type (), although that could change in the future) and it can therefore be used in expression position, such as on the right hand side of an assignment:
let _: () = while false {};
The block of a while loop is also an expression (albeit one that must also always be of unit type); this value comes from the final expression in the block—which, in your case, is the final if let:
let _: () = if let Some(rnode) = cnode.borrow().right.as_ref() {
vec.push(rnode.clone());
};
The borrow of cnode continues to the end of this expression. Since the expression is evaluated as that of the while loop's block, which is evaluated as that of the while loop expression itself, it actually lives until the while loop expression is evaluated (i.e. the while loop terminates). But your borrow of cnode must not live that long, because subsequent iterations of the loop may need to borrow it again! Hence the error, and the suggestion to add a semicolon at the end of the if let expression (thus converting it into a statement and terminating the borrow of cnode before the end of the while loop block):
if let Some(rnode) = cnode.borrow().right.as_ref() {
vec.push(rnode.clone());
};
Perhaps Rust could/should be more intelligent here, and recognise the borrow cannot possibly be required for so long, but no doubt that would add considerable additional complexity and may make it harder to introduce future changes to the language in a backwards-compatible way.
Related
I'm trying to implement an EventListener like interface in rust. I have a trait that takes a callback, the callback should mutate a variable from the scope it was defined in. But I get an error saying the borrowed value does not live long enough.
pub struct Target<T> {
funcs: Vec<Box<dyn FnMut(T) -> ()>>,
}
impl<T: Clone + 'static> Target<T> {
pub fn new() -> Target<T> {
return Target { funcs: Vec::new() };
}
pub fn add_listener(&mut self, func: Box<dyn FnMut(T) -> ()>) -> () {
self.funcs.push(Box::new(func));
}
pub fn trigger(&mut self, x: T) {
for callback in &mut self.funcs {
callback(x.clone());
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn trigger_mutation() {
let mut a = 0;
let mut t: Target<i32> = Target::new();
t.add_listener(Box::new(|x: i32| {
println!("{}", x);
a = x;
}));
t.trigger(42);
let b = a.clone();
assert_eq!(b, 42);
}
}
I run it and get this:
$ cargo test -- --nocapture
Compiling luma-connector v0.1.0 (/home/blake/git/connector)
error[E0597]: `a` does not live long enough
--> src/target.rs:32:13
|
30 | t.add_listener(Box::new(|x: i32| {
| - -------- value captured here
| ________________________|
| |
31 | | println!("{}", x);
32 | | a = x + 1;
| | ^ borrowed value does not live long enough
33 | | }));
| |__________- cast requires that `a` is borrowed for `'static`
...
37 | }
| - `a` dropped here while still borrowed
But I get an error saying the borrowed value does not live long enough.
Well yes, your typing of Target implies nothing about scoping, so as far as the Rust typesystem is concerned, the closure could just fly into space unbouded by time (e.g. add_listener could pass it to a separate thread), therefore outlive trigger_mutation, which means a does not live long enough.
There are two ways to resolve this issue:
Use Arc/Rc with interior mutability (resp. Mutex and RefCell) in order to relax the lifetime of a: Arc version[0], Rc version, this is probably the simplest, and the least restrictive on Target, though it comes at a runtime cost.
Alternatively you can "scope" Target to tell Rust that it doesn't escape, therefore everything's perfectly kosher. I'm not sure it's the best way (hopefully somebody else can contribute that information) but bounding the FnMuts on a lifetime will allow rustc to reason about this: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e67a4ab0faa8cc5d01c75293623c9fb4
This is basically free at runtime, but it means Target can't really escape its function.
So the former is probably what you want, the latter seems not super-useful for an events / notification system, but YMMV.
[0] an Atomic* would also work for the Arc version and be a bit easier & cheaper than a mutex, though it probably isn't very relevant for a test case.
Suppose I have several structures like in the following example, and in the next() method I need to pull the next event using a user-provided buffer, but if this event is a comment, and ignore comments flag is set to true, I need to pull the next event again:
struct Parser {
ignore_comments: bool,
}
enum XmlEvent<'buf> {
Comment(&'buf str),
Other(&'buf str),
}
impl Parser {
fn next<'buf>(&mut self, buffer: &'buf mut String) -> XmlEvent<'buf> {
let result = loop {
buffer.clear();
let temp_event = self.parse_outside_tag(buffer);
match temp_event {
XmlEvent::Comment(_) if self.ignore_comments => {}
_ => break temp_event,
}
};
result
}
fn parse_outside_tag<'buf>(&mut self, _buffer: &'buf mut String) -> XmlEvent<'buf> {
unimplemented!()
}
}
This code, however, gives a double borrow error, even when I have #![feature(nll)] enabled:
error[E0499]: cannot borrow `*buffer` as mutable more than once at a time
--> src/main.rs:14:13
|
14 | buffer.clear();
| ^^^^^^ second mutable borrow occurs here
15 |
16 | let temp_event = self.parse_outside_tag(buffer);
| ------ first mutable borrow occurs here
|
note: borrowed value must be valid for the lifetime 'buf as defined on the method body at 12:5...
--> src/main.rs:12:5
|
12 | fn next<'buf>(&mut self, buffer: &'buf mut String) -> XmlEvent<'buf> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0499]: cannot borrow `*buffer` as mutable more than once at a time
--> src/main.rs:16:53
|
16 | let temp_event = self.parse_outside_tag(buffer);
| ^^^^^^ mutable borrow starts here in previous iteration of loop
|
note: borrowed value must be valid for the lifetime 'buf as defined on the method body at 12:5...
--> src/main.rs:12:5
|
12 | fn next<'buf>(&mut self, buffer: &'buf mut String) -> XmlEvent<'buf> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors
I can (approximately at least) understand why an error could happen here with the NLL feature turned off, but I don't understand why it happens with NLL.
Anyway, my end goal is to implement this without flags, so I also tried doing this (it is recursive, which is really unfortunate, but all non-recursive versions I came up with could not possibly work without NLL):
fn next<'buf>(&mut self, buffer: &'buf mut String) -> XmlEvent<'buf> {
buffer.clear();
{
let temp_event = self.parse_outside_tag(buffer);
match temp_event {
XmlEvent::Comment(_) if self.ignore_comments => {}
_ => return temp_event,
}
}
self.next(buffer)
}
Here I tried to confine the borrow inside a lexical block, and nothing from this block leaks to the outside. However, I'm still getting an error:
error[E0499]: cannot borrow `*buffer` as mutable more than once at a time
--> src/main.rs:23:19
|
15 | let temp_event = self.parse_outside_tag(buffer);
| ------ first mutable borrow occurs here
...
23 | self.next(buffer)
| ^^^^^^ second mutable borrow occurs here
24 | }
| - first borrow ends here
error: aborting due to previous error
And again, NLL does not fix it.
It has been a long time since I encountered a borrow checking error which I don't understand, so I'm hoping it is actually something simple which I'm overlooking for some reason :)
I really suspect that the root cause is somehow connected with the explicit 'buf lifetime (in particular, errors with the NLL flag turned on have these notes about it), but I can't understand what exactly is wrong here.
This is a limitation of the current implementation of non-lexical lifetimes This can be shown with this reduced case:
fn next<'buf>(buffer: &'buf mut String) -> &'buf str {
loop {
let event = parse(buffer);
if true {
return event;
}
}
}
fn parse<'buf>(_buffer: &'buf mut String) -> &'buf str {
unimplemented!()
}
fn main() {}
This limitation prevents NLL case #3: conditional control flow across functions
In compiler developer terms, the current implementation of non-lexical lifetimes is "location insensitive". Location sensitivity was originally available but it was disabled in the name of performance.
I asked Niko Matsakis about this code:
In the context of your example: the value event only has to have the lifetime 'buf conditionally — at the return point which may or may not execute. But when we are "location insensitive", we just track the lifetime that event must have anywhere, without considering where that lifetime must hold. In this case, that means we make it hold everywhere, which is why you get a compilation failure.
One subtle thing is that the current analysis is location sensitive in one respect — where the borrow takes place. The length of the borrow is not.
The good news is that adding this concept of location sensitivity back is seen as an enhancement to the implementation of non-lexical lifetimes. The bad news:
That may or may not be before the [Rust 2018] edition.
(Note: it did not make it into the initial release of Rust 2018)
This hinges on a (even newer!) underlying implementation of non-lexical lifetimes that improves the performance. You can opt-in to this half-implemented version using -Z polonius:
rustc +nightly -Zpolonius --edition=2018 example.rs
RUSTFLAGS="-Zpolonius" cargo +nightly build
Because this is across functions, you can sometimes work around this by inlining the function.
I posted a question (A borrow checker problem with a loop and non-lexical lifetimes) that was answered by the answer of this question.
I'll document here a workaround that also answers the question. Let's say you have code like this, that only compiles with Polonius:
struct Inner;
enum State<'a> {
One,
Two(&'a ()),
}
fn get<'s>(_inner: &'s mut Inner) -> State<'s> {
unimplemented!()
}
struct Outer {
inner: Inner,
}
impl Outer {
pub fn read<'s>(&'s mut self) -> &'s () {
loop {
match get(&mut self.inner) {
State::One => (), // In this case nothing happens, the borrow should end and the loop should continue
State::Two(a) => return a, // self.inner ought to be borrowed for 's, that's just to be expected
}
}
}
}
As it was said in the another answer:
One subtle thing is that the current analysis is location sensitive in one respect — where the borrow takes place. The length of the borrow is not.
Indeed, borrowing the needed reference again inside the conditional branch makes it compile! Of course, this makes the assumption that get is referentially transparent, so your mileage may vary, but borrowing again seems like an easy enough workaround.
struct Inner;
enum State<'a> {
One,
Two(&'a ()),
}
fn get<'s>(_inner: &'s mut Inner) -> State<'s> {
unimplemented!()
}
struct Outer {
inner: Inner,
}
impl Outer {
pub fn read<'s>(&'s mut self) -> &'s () {
loop {
match get(&mut self.inner) {
State::One => (), // In this case nothing happens, the borrow should end and the loop should continue
State::Two(a) => {
return match get(&mut self.inner) { // Borrowing again compiles!
State::Two(a) => a,
_ => unreachable!(),
}
}, // self.inner ought to be borrowed for 's, that's just to be expected
}
}
}
}
fn main() {
println!("Hello, world!");
}
I've very recently started studying Rust, and while working on a test program, I wrote this method:
pub fn add_transition(&mut self, start_state: u32, end_state: u32) -> Result<bool, std::io::Error> {
let mut m: Vec<Page>;
let pages: &mut Vec<Page> = match self.page_cache.get_mut(&start_state) {
Some(p) => p,
None => {
m = self.index.get_pages(start_state, &self.file)?;
&mut m
}
};
// omitted code that mutates pages
// ...
Ok(true)
}
it does work as expected, but I'm not convinced about the m variable. If I remove it, the code looks more elegant:
pub fn add_transition(&mut self, start_state: u32, end_state: u32) -> Result<bool, std::io::Error> {
let pages: &mut Vec<Page> = match self.page_cache.get_mut(&start_state) {
Some(p) => p,
None => &mut self.index.get_pages(start_state, &self.file)?
};
// omitted code that mutates pages
// ...
Ok(true)
}
but I get:
error[E0716]: temporary value dropped while borrowed
--> src\module1\mod.rs:28:29
|
26 | let pages: &mut Vec<Page> = match self.page_cache.get_mut(&start_state) {
| _____________________________________-
27 | | Some(p) => p,
28 | | None => &mut self.index.get_pages(start_state, &self.file)?
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
| | | |
| | | temporary value is freed at the end of this statement
| | creates a temporary which is freed while still in use
29 | | };
| |_________- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
I fully understand the error, which directed me to the working snippet, but I'm wondering if there's a more elegant and/or idiomatic way of writing this code. I am declaring m at the beginning of the function, only to prevent a temporary variable from being freed too early. Is there a way of telling the compiler that the lifetime of the return value of self.index.get_pages should be the whole add_transition function?
Further details:
Page is a relatively big struct, so I'd rather not implement the Copy trait nor I'd clone it.
page_cache is of type HashMap<u32, Vec<Page>>
self.index.get_pages is relatively slow and I'm using page_cache to cache results
The return type of self.index.get_pages is Result<Vec<Page>, std::io::Error>
This is normal, your 'cleaner' code basically comes down to do something as follows:
let y = {
let x = 42;
&x
};
Here it should be obvious that you cannot return a reference to x because x is dropped at the end of the block. Those rules don't change when working with temporary values: self.index.get_pages(start_state, &self.file)? creates a temporary value that is dropped at the end of the block (line 29) and thus you can't return a reference to it.
The workaround via m now moves that temporary into the m binding one block up which will live long enough for pages to work with it.
Now for alternatives, I guess page_cache is a HashMap? Then you could alternatively do something like let pages = self.page_cache.entry(start_state).or_insert_with(||self.index.get_pages(...))?;. The only problem with that approach is that get_pages returns a Result while the current cache stores Vec<Page> (the Ok branch only). You could adapt the cache to actually store Result instead, which I think is semantically also better since you want to cache the results of that function call, so why not do that for Err? But if you have a good reason to not cache Err, the approach you have should work just fine.
Yours is probably the most efficient way, but in theory not necessary, and one can be more elegant.
Another way of doing it is to use a trait object in this case — have the variable be of the type dyn DerefMut<Vec<Page>>. This basically means that this variable can hold any type that implements the trait DerefMut<Vec<Page>>>, two types that do so are &mut Vec<Page> and Vec<Page>, in that case the variable can hold either of these, but the contents can only be referenced via DerefMut.
So the following code works as an illustration:
struct Foo {
inner : Option<Vec<i32>>,
}
impl Foo {
fn new () -> Self {
Foo { inner : None }
}
fn init (&mut self) {
self.inner = Some(Vec::new())
}
fn get_mut_ref (&mut self) -> Option<&mut Vec<i32>> {
self.inner.as_mut()
}
}
fn main () {
let mut foo : Foo = Foo::new();
let mut m : Box<dyn AsMut<Vec<i32>>> = match foo.get_mut_ref() {
Some(r) => Box::new(r),
None => Box::new(vec![1,2,3]),
};
m.as_mut().as_mut().push(4);
}
The key here is the type Box<dyn AsMut<Vec<i32>>; this means that it can be a box that holds any type, so long the type implement AsMut<Vec<i32>>, because it's boxed in we also need .as_mut().as_mut() to get the actual &mut <Vec<i32>> out of it.
Because different types can have different sizes; they also cannot be allocated on the stack, so they must be behind some pointer, a Box is typically chosen therefore, and in this case necessary, a normal pointer that is sans ownership of it's pointee will face similar problems to those you face.
One might argue that this code is more elegant, but yours is certainly more efficient and does not require further heap allocation.
In Rust, I have a BTreeSet that I'm using to keep my values in order. I have a loop that should retrieve and remove the first (lowest) member of the set. I'm using a cloned iterator to retrieve the first member. Here's the code:
use std::collections::BTreeSet;
fn main() {
let mut start_nodes = BTreeSet::new();
// add items to the set
while !start_nodes.is_empty() {
let mut start_iter = start_nodes.iter();
let mut start_iter_cloned = start_iter.cloned();
let n = start_iter_cloned.next().unwrap();
start_nodes.remove(&n);
}
}
This, however, gives me the following compile error:
error[E0502]: cannot borrow `start_nodes` as mutable because it is also borrowed as immutable
--> prog.rs:60:6
|
56 | let mut start_iter = start_nodes.iter();
| ----------- immutable borrow occurs here
...
60 | start_nodes.remove(&n);
| ^^^^^^^^^^^ mutable borrow occurs here
...
77 | }
| - immutable borrow ends here
Why is start_nodes.iter() considered an immutable borrow? What approach should I take instead to get the first member?
I'm using version 1.14.0 (not by choice).
Why is start_nodes.iter() considered an immutable borrow?
Whenever you ask a question like this one, you need to look at the prototype of the function, in this case the prototype of BTreeSet::iter():
fn iter(&self) -> Iter<T>
If we look up the Iter type that is returned, we find that it's defined as
pub struct Iter<'a, T> where T: 'a { /* fields omitted */ }
The lifetime 'a is not explicitly mentioned in the definition of iter(); however, the lifetime elision rules make the function definition equivalent to
fn iter<'a>(&'a self) -> Iter<'a, T>
From this expanded version, you can see that the return value has a lifetime that is bound to the lifetime of the reference to self that you pass in, which is just another way of stating that the function call creates a shared borrow that lives as long as the return value. If you store the return value in a variable, the borrow lives at least as long as the variable.
What approach should I take instead to get the first member?
As noted in the comments, your code works on recent versions of Rust due to non-lexical lifetimes – the compiler figures out by itself that start_iter and start_iter_cloned don't need to live longer than the call to next(). In older versions of Rust, you can artificially limit the lifetime by introducing a new scope:
while !start_nodes.is_empty() {
let n = {
let mut start_iter = start_nodes.iter();
let mut start_iter_cloned = start_iter.cloned();
start_iter_cloned.next().unwrap()
};
start_nodes.remove(&n);
}
However, note that this code is needlessly long-winded. The new iterator you create and its cloning version only live inside the new scope, and they aren't really used for any other purpose, so you could just as well write
while !start_nodes.is_empty() {
let n = start_nodes.iter().next().unwrap().clone();
start_nodes.remove(&n);
}
which does exactly the same, and avoids the issues with long-living borrows by avoiding to store the intermediate values in variables, to ensure their lifetime ends immediately after the expression.
Finally, while you don't give full details of your use case, I strongly suspect that you would be better off with a BinaryHeap instead of a BTreeSet:
use std::collections::BinaryHeap;
fn main() {
let mut start_nodes = BinaryHeap::new();
start_nodes.push(42);
while let Some(n) = start_nodes.pop() {
// Do something with `n`
}
}
This code is shorter, simpler, completely sidesteps the issue with the borrow checker, and will also be more efficient.
Not sure this is the best approach, but I fixed it by introducing a new scope to ensure that the immutable borrow ends before the mutable borrow occurs:
use std::collections::BTreeSet;
fn main() {
let mut start_nodes = BTreeSet::new();
// add items to the set
while !start_nodes.is_empty() {
let mut n = 0;
{
let mut start_iter = start_nodes.iter();
let mut start_iter_cloned = start_iter.cloned();
let x = &mut n;
*x = start_iter_cloned.next().unwrap();
}
start_nodes.remove(&n);
}
}
I am implementing a singly-linked list. I want to add a method add_all_at_index which will take a new list and an index, insert the new list at the specified index and rearrange the tail of the current list to follow after the new list.
Assume the current list is is [1,2,3,4,5]. Calling add_all_at_index at position 2 with list [8,9,10] should result in [1,2,8,9,10,3,4,5].
I'm especially having trouble in assigning the old tail of the list after the new list. I do not know how to append [3,4,5] to the node with value 10 in the list.
The algorithm I am trying to implement is
Save the current next of the node at (index - 1) in a variable called current_next
Set the next of node at (index - 1) to the head of the list
Iterate to the last of the newly added list and set its next to the current_next.
I am having trouble accomplishing the last step. Below is the code I came up with:
use std::fmt::*;
fn main() {
let list: List<i32> = List::new();
}
#[derive(PartialEq, Debug)]
pub struct Node<T: Debug> {
pub element: T,
pub next: Option<Box<Node<T>>>,
}
#[derive(PartialEq, Debug)]
pub struct List<T: Debug> {
pub head: Option<Node<T>>,
}
impl<T: Debug> List<T> {
pub fn new() -> Self {
List { head: None }
}
pub fn add_all_at_index(&mut self, list_to_add: List<T>, index: usize) {
if index > 0 {
let nth_node = self.get_nth_node_mut(index).take(); // Gets a mutable reference to node at index
nth_node.map(|node| {
let current_next = node.next.take(); // I store a reference to the next of nth node,
node.next = list_to_add.head.map(|node| Box::new(node));
// The 3rd step in the algorithm I mentioned above.
let last_node = self.get_nth_node_mut(self.length()); // This line does not compile. Getting multiple errors in this line
last_node.map(|node| node.next = current_next);
});
} else {
self.head = list_to_add.head
}
}
fn get_nth_node_mut(&mut self, n: usize) -> Option<&mut Node<T>> {
let mut nth_node = self.head.as_mut();
for _ in 0..n {
nth_node = match nth_node {
None => return None,
Some(node) => node.next.as_mut().map(|node| &mut **node),
}
}
nth_node
}
pub fn length(&self) -> usize {
let mut count = 0;
let mut current_node = self.head.as_ref();
while let Some(node) = current_node {
count = count + 1;
current_node = node.next.as_ref().map(|node| &**node)
}
count
}
}
The error I am getting is
warning: unused variable: `list`
--> src/main.rs:4:9
|
4 | let list: List<i32> = List::new();
| ^^^^
|
= note: #[warn(unused_variables)] on by default
= note: to avoid this warning, consider using `_list` instead
error[E0500]: closure requires unique access to `self` but `*self` is already borrowed
--> src/main.rs:26:26
|
25 | let nth_node = self.get_nth_node_mut(index).take(); // Gets a mutable reference to node at index
| ---- borrow occurs here
26 | nth_node.map(|node| {
| ^^^^^^ closure construction occurs here
...
31 | let last_node = self.get_nth_node_mut(self.length()); // This line does not compile. Getting multiple errors in this line
| ---- borrow occurs due to use of `self` in closure
...
34 | } else {
| - borrow ends here
error[E0502]: cannot borrow `**self` as immutable because it is also borrowed as mutable
--> src/main.rs:31:55
|
31 | let last_node = self.get_nth_node_mut(self.length()); // This line does not compile. Getting multiple errors in this line
| ---- ^^^^ immutable borrow occurs here
| |
| mutable borrow occurs here
32 | last_node.map(|node| node.next = current_next);
33 | });
| - mutable borrow ends here
Is this even the right approach to implement add_all_at_index?
I even tried implementing an iterator that returns a mutable reference to a node but I was not able to do that either. I have pasted the entire code in a gist at https://gist.github.com/hardvain/32fca033bb61a5e3bf8bbeeb32fbbd5e
First, the solution:
pub fn add_all_at_index(&mut self, list_to_add: List<T>, index: usize) {
if index > 0 {
let tail = {
let nth_node = self.get_nth_node_mut(index).take();
nth_node.map(|node| {
let current_next = node.next.take();
node.next = list_to_add.head.map(|node| Box::new(node));
current_next
})
};
if let Some(current_next) = tail {
let n = self.length();
let last_node = self.get_nth_node_mut(n);
last_node.map(|node| node.next = current_next);
}
} else {
self.head = list_to_add.head
}
}
Ugly, right? Right. Getting this working required several changes:
I moved step 3 (reattaching the tail of the list) outside of the closure passed to map, so that nth_node (which borrows self) wouldn't still be alive when you try to borrow self again to get the length.
I therefore had to save current_next, so I had the closure return it, and stored the result of map in a new variable called tail. So tail is an Option<Box<Node<T>>>.
I wrapped the tail-reattaching part in an if let to destructure tail and get current_next back out.
Then, I separated self.get_nth_node_mut(self.length()) into two statements to resolve the remaining borrowing error.
Some followup suggestions:
Using .map() for side effects and then ignoring the return value is unidiomatic. Use if let to run code on the content of an Option.
You're ignoring all the None cases. If you try to call add_all_at_index with an index that is out of range, nothing happens to self and list_to_add is just lost. The function should probably return a Result of some kind, or perhaps a bool.
.take() is for getting an Option<T> out of a &mut Option<T>. It doesn't do anything useful to an Option<&mut T>.
|node| Box::new(node) is just Box::new.
Because the first node is boxed but none of the others are, you will have to write a lot of special case code, and there's not a transparent conversion between a Node and a List.
Using get_nth_node_mut in the implementation of add_all_at_index almost forces you to traverse the list twice. Because it's implemented on List instead of on Node, you can't easily get a reference to the last element of the list, so you end up calling length() (making the total number of traversals 3) and then get_nth_node_mut again to dig up the last element.
Some of the ugliness can be mitigated by careful interface design -- for instance, this method becomes cleaner if List has a split_at_index method -- but some of it is just due to the fact that linked lists are ugly. Particularly ugly in Rust, because the language forbids shared mutable references, even temporarily. You would have to use unsafe to write many linked list operations in Rust the same way you would in C.
If you have not already, please read Learning Rust With Entirely Too Many Linked Lists. This book addresses many of the subtleties that arise when you try to implement a linked list in Rust.