Rust - 内存管理 - 弹出所有权 - 链表

问题描述 投票:0回答:1

过去几天我一直在自学 Rust。今天我想尝试一下一个简单的链表。我的工作代码如下。

我有两个问题,关于下面的 List::pop() 函数。

  1. 如果没有clone()语句,我无法让程序运行。我确信一定可以做到这一点并维持该物品的一个所有者(无需诉诸不安全模式)。有什么建议吗?

  2. 我不清楚我是否已管理与使用 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 memory-management ownership
1个回答
0
投票

今天我想尝试一下一个简单的链表。

由于所有权的原因,链表在 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

尚未被消耗,当它超出范围时,它将被隐式删除。

顺便说一句,从风格的角度来看,你的代码过于受限,这是不必要的:

除非绝对必要,否则通常不会在类型上设置特征约束,但这里不是这种情况
  • 你应该根据需要在 impl 边界后面有尽可能少的方法,在你的列表 impl 中只有
  • print
  • 需要
    T: Display
    ,所以与其将所有内容都放在
    impl<T: Display>
    块中,你应该在这样的块中单独使用
    print 
     ,以及无约束 
    impl <T>
    块中的所有其他方法,因为它们不关心
    T
    的显示。
    
        
© www.soinside.com 2019 - 2024. All rights reserved.