Rust 中的单链表

Posted

技术标签:

【中文标题】Rust 中的单链表【英文标题】:Singly-Linked List in Rust 【发布时间】:2022-01-09 14:44:12 【问题描述】:

我最近一直在尝试自学一些 Rust,并想通过实现一个简单的链表来练习一下。我从 Rust 库的链表中获得了一些灵感,并尝试复制我已经理解的部分。我还决定暂时将其设为单链接。

struct Node<T> 
    element: T,
    next: Option<Box<Node<T>>>,


impl<T> Node<T> 
    fn new(element: T) -> Self 
        Node 
            element: element,
            next: None,
        
    

    fn append(&mut self, element: Box<Node<T>>) 
        self.next = Some(element);
    


pub struct LinkedList<T> 
    head: Option<Box<Node<T>>>,
    tail: Option<Box<Node<T>>>,
    len: u32,


impl<T> LinkedList<T> 
    pub fn new() -> Self 
        head: None,
        tail: None,
        len: 0,
    

    pub fn push(&mut self, element: T) 
        let node: Box<Node<T>> = Box::new(Node::new(element));

        match self.tail 
            None => self.head = Some(node),
            Some(mut ref tail) => tail.append(node),
        
        self.tail = Some(node);
        self.len += 1;
    

    pub fn pop(&mut self) -> Option<T> 
        //not implemented
    

    pub fn get(&self, index: u32) -> Option<T> 
        //not implemented
    

这是我到目前为止所得到的;据我了解,这段代码的问题是Box 不能有多个引用以保护内存安全。

所以当我将列表头设置为节点时

None => self.head = Some(node),

我不能继续设置

self.tail = Some(node);

稍后,我的理解到目前为止是否正确?这样做的正确方法是什么?我必须像在库中那样使用Shared,还是有办法可以使用Box 或其他类型?

【问题讨论】:

obligatory reference (Learning Rust With Entirely Too Many Linked Lists) 请注意,传统的单链接(Cons List)不会记住tail。这是一种允许在 O(1) 中追加的方法,但不是必需的。 @Oliver 如果它回答了你的问题,你能接受 Ijedrzs 的回答吗?否则请说明缺少的内容。 【参考方案1】:

你可以使用比这更简单的东西,只使用你的节点

use std::fmt;

struct Payload 
  id: i32,
  value: i32,


impl fmt::Display for Payload 
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
        write!(f, "(, )", self.id, self.value)
    


struct Node<T> 
    element: T,
    next: Option<Box<Node<T>>>,


impl<T> Node<T> where T: std::fmt::Display
    fn new(element: T) -> Self 
        Node 
            element: element,
            next: None,
        
    

    fn append(&mut self, element: T) 
        match &mut self.next 
            None => let n = Node 
                        element: element,
                        next: None,
                    ;
                    self.next = Some(Box::new(n));
            ,
            Some(ref mut x) => x.append(element),
        
    

    fn list(& self) 
        println!("", self.element);
        match &self.next 
            None => ,
            Some(x) => x.list(),
        
    


fn main()
  let mut h = Node::new(Payload id:1, value:1);
  h.append(Payload id:2, value:2);
  h.append(Payload id:3, value:3);
  h.append(Payload id:4, value:4);
  h.append(Payload id:5, value:5);
  h.list();
  h.append(Payload id:6, value:6);
  h.list();

【讨论】:

所以它是递归的append,它是非性能的 @prehistoricpenguin 如果我们只迭代元素并执行它而不是递归会更好吗?【参考方案2】:

您的问题是您在移动某个值 (node) 后尝试使用它;由于Box&lt;Node&lt;T&gt;&gt; 没有实现Copy,当你在match 表达式中使用它时:

match self.tail 
    None => self.head = Some(node),
    Some(ref mut tail) => tail.append(node),

node 被移动到 self.headself.tail,以后不能再使用。除了阅读必读的 Learning Rust With Entirely Too Many Linked Lists 以了解在 Rust 中实现链表的不同方式之外,我建议您首先在 Rust 的基本概念领域做更多的研究,尤其是:

Ownership References and Borrowing What are move semantics?

【讨论】:

以上是关于Rust 中的单链表的主要内容,如果未能解决你的问题,请参考以下文章

数据结构学习笔记(单链表单循环链表带头双向循环链表)的增删查改排序等)

数据结构学习笔记(单链表单循环链表带头双向循环链表)的增删查改排序等)

Go 反转链表单链表的添加和显示单链表的添加和显示控制反转

Go 反转链表单链表的添加和显示单链表的添加和显示控制反转

[Data Structure]线性表Linear List2

408数据结构与算法—单链表