我正在实施一个单链表。我想添加一个方法add_all_at_index
,它将获取一个新列表和一个索引,在指定的索引处插入新列表,并在新列表后重新排列当前列表的尾部。
假设当前列表是[1,2,3,4,5]
。使用列表add_all_at_index
在2
位置拨打[8,9,10]
会产生[1,2,8,9,10,3,4,5]
。
我特别难以在新列表之后分配列表的旧尾部。我不知道如何将[3,4,5]
附加到列表中值为10
的节点上。
我想要实现的算法是
current_next
current_next
旁边的。我无法完成最后一步。以下是我提出的代码:
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
}
}
我得到的错误是
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
这是否是实施add_all_at_index
的正确方法?
我甚至尝试实现一个迭代器,它返回一个节点的可变引用,但我也无法做到这一点。我在https://gist.github.com/hardvain/32fca033bb61a5e3bf8bbeeb32fbbd5e
的要点中粘贴了整个代码答案 0 :(得分:2)
首先,解决方案:
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
}
}
丑陋,对吗?对。要实现这一目标需要进行一些更改:
我在传递给map
的闭包之外移动了第3步(重新附加列表的尾部),以便nth_node
(借用self
)不会当你试图再次借用self
以获得长度时,你仍然活着。
因此我必须保存current_next
,所以我让闭包返回它,并将map
的结果存储在一个名为tail
的新变量中。因此tail
是Option<Box<Node<T>>>
。
我将尾部重新附加的部分包裹在if let
中以解构tail
并让current_next
退出。
然后,我将self.get_nth_node_mut(self.length())
分成两个语句来解决剩余的借用错误。
一些后续建议:
使用.map()
进行副作用,然后忽略返回值是不明显的。使用if let
在Option
。
您忽略了所有None
个案例。如果您尝试使用超出范围的add_all_at_index
来致电index
,则self
没有任何结果,list_to_add
就会丢失。该函数应该返回某种Result
,或者bool
。
.take()
用于从Option<T>
中获取&mut Option<T>
。它对Option<&mut T>
没有任何用处。
|node| Box::new(node)
只是Box::new
。
因为第一个节点是盒装的,但其他节点都没有,所以你必须编写很多特殊的案例代码,并且节点和列表之间不是透明的转换。
在get_nth_node_mut
的实现中使用add_all_at_index
几乎会强制您遍历列表两次。由于它是在List
而非Node
上实施的,因此您无法轻松获得对列表最后一个元素的引用,因此您最终会调用length()
(使遍历的总数为3)然后再次get_nth_node_mut
来挖掘最后一个元素。
通过仔细的界面设计可以减轻一些丑陋 - 例如,如果List
具有split_at_index
方法,这种方法会变得更加清晰 - 但其中一些方法只是因为链接列表很难看。 特别在Rust中丑陋,因为语言禁止共享可变引用,甚至是暂时的。你必须使用unsafe
在Rust中编写许多链表操作,就像在C中一样。
如果您还没有,请阅读Learning Rust With Entirely Too Many Linked Lists。本书解决了当您尝试在Rust中实现链接列表时出现的许多细微之处。