过去几天我一直在自学 Rust。今天我想尝试一下一个简单的链表。我的工作代码如下。
我有两个问题,关于下面的 List::pop() 函数。
如果没有clone()语句,我无法让程序运行。我确信一定可以做到这一点并维持该物品的一个所有者(无需诉诸不安全模式)。有什么建议吗?
我不清楚我是否已管理与使用 Box 智能指针相关的堆内存。我是否主动需要这样做?如果需要,我该怎么做?
// This implements a simple generic stack as a linked list.
use std::fmt::Display;
use std::fmt::Write;
type Link<T: Display + Clone> = Option<Box<Node<T>>>;
#[derive(Debug, Clone)]
struct Node<T: Display + Clone> {
item: Box<T>,
next: Link<T>,
}
impl<T: Display + Clone> Node<T> {
pub fn new(item: T, next: Link<T>) -> Link<T> {
Some(Box::new(Node{item: Box::new(item), next: next}))
}
pub fn append_to_string(&self, mut s: String) -> String {
write!(s, "{}", *self.item);
s
}
}
#[derive(Debug, Clone)]
struct List<T: Display + Clone> {
head: Link<T>,
len: usize,
}
impl<T: Display + Clone> List<T> {
pub fn new() -> Self {
List{head: None, len: 0_usize}
}
pub fn len(&self) -> usize {
self.len
}
pub fn push(&mut self, item: T) {
let mut new = Node::new(item, self.head.take());
self.head = new;
self.len += 1;
}
pub fn pop(&mut self) -> Result<T, &str> {
let current = &mut self.head;
match current {
None => Err("The List is empty."),
Some(node) => {
let item = *node.item.clone(); // The clone kludge
*current = node.next.take(); // drop the popped node
// Nagging question: is the old node
// [box memory on the heap]
// cleaned up - or do I need to do it?
self.len -= 1; // decrement the length
Ok(item)
},
}
}
pub fn print(&self) {
let mut s = String::from("The list: (");
let mut p = &self.head;
let mut virgin = true;
while let Some(box_node) = p {
s += if !virgin {", "} else {""};
s = box_node.append_to_string(s);
virgin = false;
p = &box_node.next;
}
s += ")";
println!("{}", s)
}
}
fn push(list: &mut List<i32>, from: i32, to:i32) {
// push a few numbers onto a list of type List<i32>
for n in from..to {
list.push(n);
println!("Pushed: {}", n);
}
}
fn main() {
// Test: construct a list of i32, push and pop some numbers
let mut list: List<i32> = List::new();
push(&mut list, 1, 4);
list.print();
loop {
match list.pop() {
Ok(result) => println!("Popped: {}", result),
Err(message) => {
println!("Error while trying to pop: {}", message);
break;
},
}
}
list.print();
push(&mut list, 1001, 1006);;
println!("Length: {}", list.len());
list.print();
}
今天我想尝试一下一个简单的链表。
由于所有权的原因,链表在 Rust 中是一个不平凡的话题,也是学习这门语言的绝对糟糕的方式。但如果这就是你想要的,有一个名为“使用太多链表学习 Rust”的广泛指南,其中涵盖了相当简单的链表和更复杂的双向链表。 无论如何
如果没有clone()语句,我就无法让程序运行。我确信一定可以做到这一点并维持该物品的一个所有者(无需诉诸不安全模式)。有什么建议吗?
仔细应用通过理解和
(因为它们会产生奇怪的瞬态状态,因此确保在恢复正确状态之前不会发生恐慌非常重要)std::mem
函数系列
std::mem::swap
和“方便包装纸”
std::mem::replace
和
std::mem::take
:
pub fn pop(&mut self) -> Result<T, &str> {
let node = std::mem::take(&mut self.head).ok_or("The list is empty.")?;
self.head = node.next;
self.len -= 1;
Ok(*node.item)
}
我不清楚我是否管理了与使用 Box 智能指针相关的堆内存。我是否主动需要这样做?如果需要,我该怎么做?你不需要做任何事情,如果
Box
尚未被消耗,当它超出范围时,它将被隐式删除。
顺便说一句,从风格的角度来看,你的代码过于受限,这是不必要的:除非绝对必要,否则通常不会在类型上设置特征约束,但这里不是这种情况
print
T: Display
,所以与其将所有内容都放在 impl<T: Display>
块中,你应该在这样的块中单独使用 print
,以及无约束
impl <T>
块中的所有其他方法,因为它们不关心 T
的显示。