rust手写LinkedList

1. LinkedList数据结构

链表是基础数据结构,本质上是用离散的内存,用指针将数据串在一起。一个链表数据结构,从数据结构上,有LinkedList和Node两个抽象数据对象,操作一般有push_front、push_back、is_empty、len等。

写rust之前,先写一个python的LInkedList作为对比

class Node:
    def __init__(self, data=None):
        self.data = data
        self.next = None

class LinkedList:
    def __init__(self):
        self.head = None

    def insert_at_beginning(self, data):
        new_node = Node(data)
        new_node.next = self.head
        self.head = new_node

    def insert_at_end(self, data):
        if not self.head:
            self.head = Node(data)
            return
        cur = self.head
        while cur.next:
            cur = cur.next
        cur.next = Node(data)

    def delete_node(self, key):
        cur = self.head
        if cur and cur.data == key:
            self.head = cur.next
            cur = None
            return
        prev = None
        while cur and cur.data != key:
            prev = cur
            cur = cur.next
        if cur is None:
            return
        prev.next = cur.next
        cur = None

    def search(self, key):
        cur = self.head
        while cur:
            if cur.data == key:
                return True
            cur = cur.next
        return False

    def display(self):
        cur = self.head
        while cur:
            print(cur.data, end=' ')
            cur = cur.next
        print()

# 测试代码
linked_list = LinkedList()
linked_list.insert_at_beginning(1)
linked_list.insert_at_beginning(2)
linked_list.insert_at_end(3)
linked_list.display()  # 输出:2 1 3
linked_list.delete_node(1)
linked_list.display()  # 输出:2 3
print(linked_list.search(3))  # 输出:True
print(linked_list.search(4))  # 输出:False

这里一般使用abstract api思想,来封装LinkedList数据结构,python写是比较直观简单的。

2. rust写LinkedList

2.1 简化版LinkedList

如下是个rust源代码中扒拉出的简单版本的LinkedList

use core::ptr::NonNull;
use core::marker::PhantomData;
use core::mem;
use core::fmt;

struct MyNode<T> {
    next: Option<NonNull<MyNode<T>>>,
    prev: Option<NonNull<MyNode<T>>>,
    element: T,
}

pub struct MyLinkedList<T> {
    head: Option<NonNull<MyNode<T>>>,
    tail: Option<NonNull<MyNode<T>>>,
    len: usize,
    marker: PhantomData<Box<MyNode<T>>>,
}

pub struct MyIter<'a, T: 'a> {
    head: Option<NonNull<MyNode<T>>>,
    tail: Option<NonNull<MyNode<T>>>,
    len: usize,
    marker: PhantomData<&'a MyNode<T>>,
}

impl<'a, T> Iterator for MyIter<'a, T> {
    type Item = &'a T;

    #[inline]
    fn next(&mut self) -> Option<&'a T> {
        if self.len == 0 {
            None
        } else {
            self.head.map(|node| unsafe {
                // Need an unbound lifetime to get 'a
                let node = &*node.as_ptr();
                self.len -= 1;
                self.head = node.next;
                &node.element
            })
        }
    }
}

impl<T> Clone for MyIter<'_, T> {
    fn clone(&self) -> Self {
        MyIter { ..*self }
    }
}

impl<T> MyNode<T> {
    fn new(element: T) -> Self {
        MyNode { next: None, prev: None, element }
    }

    fn into_element(self: Box<Self>) -> T {
        self.element
    }
}

impl<T: fmt::Debug> fmt::Debug for MyLinkedList<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_list().entries(self).finish()
    }
}

pub struct IntoIter<T> {
    list: MyLinkedList<T>,
}

impl<T: fmt::Debug> fmt::Debug for IntoIter<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("IntoIter").field(&self.list).finish()
    }
}

impl<T> IntoIterator for MyLinkedList<T> {
    type Item = T;
    type IntoIter = IntoIter<T>;

    /// Consumes the list into an iterator yielding elements by value.
    #[inline]
    fn into_iter(self) -> IntoIter<T> {
        IntoIter { list: self }
    }
}

impl<'a, T> IntoIterator for &'a MyLinkedList<T> {
    type Item = &'a T;
    type IntoIter = MyIter<'a, T>;

    fn into_iter(self) -> MyIter<'a, T> {
        self.iter()
    }
}

impl<T> Iterator for IntoIter<T> {
    type Item = T;

    #[inline]
    fn next(&mut self) -> Option<T> {
        self.list.pop_front()
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.list.len, Some(self.list.len))
    }
}


impl<T> Default for MyLinkedList<T> {
    /// Creates an empty `LinkedList<T>`.
    #[inline]
    fn default() -> Self {
        Self::new()
    }
}

impl<T> MyLinkedList<T> {
    #[inline]
    #[must_use]
    pub const fn new() -> Self {
        MyLinkedList { head: None, tail: None, len: 0, marker: PhantomData }
    }

    #[inline]
    fn pop_front_node(&mut self) -> Option<Box<MyNode<T>>> {
        // This method takes care not to create mutable references to whole nodes,
        // to maintain validity of aliasing pointers into `element`.
        self.head.map(|node| unsafe {
            let node = Box::from_raw(node.as_ptr());
            self.head = node.next;

            match self.head {
                None => self.tail = None,
                // Not creating new mutable (unique!) references overlapping `element`.
                Some(head) => (*head.as_ptr()).prev = None,
            }

            self.len -= 1;
            node
        })
    }

    /// Adds the given node to the back of the list.
    #[inline]
    fn push_back_node(&mut self, mut node: Box<MyNode<T>>) {
        // This method takes care not to create mutable references to whole nodes,
        // to maintain validity of aliasing pointers into `element`.
        unsafe {
            node.next = None;
            node.prev = self.tail;
            let node = Some(Box::leak(node).into());

            match self.tail {
                None => self.head = node,
                // Not creating new mutable (unique!) references overlapping `element`.
                Some(tail) => (*tail.as_ptr()).next = node,
            }

            self.tail = node;
            self.len += 1;
        }
    }

    pub fn pop_front(&mut self) -> Option<T> {
        self.pop_front_node().map(MyNode::into_element)
    }

    #[inline]
    pub fn iter(&self) -> MyIter<'_, T> {
        MyIter { head: self.head, tail: self.tail, len: self.len, marker: PhantomData }
    }

    #[inline]
    #[must_use]
    pub fn is_empty(&self) -> bool {
        self.head.is_none()
    }

    #[inline]
    #[must_use]
    pub fn len(&self) -> usize {
        self.len
    }

    #[inline]
    pub fn clear(&mut self) {
        *self = Self::new();
    }

    pub fn contains(&self, x: &T) -> bool
    where
        T: PartialEq<T>,
    {
        self.iter().any(|e| e == x)
    }

    #[inline]
    #[must_use]
    pub fn front(&self) -> Option<&T> {
        unsafe { self.head.as_ref().map(|node| &node.as_ref().element) }
    }


    #[inline]
    #[must_use]
    pub fn back(&self) -> Option<&T> {
        unsafe { self.tail.as_ref().map(|node| &node.as_ref().element) }
    }

    pub fn push_back(&mut self, elt: T) {
        self.push_back_node(Box::new(MyNode::new(elt)));
    }
}



impl<T> Drop for MyLinkedList<T> {
    fn drop(&mut self) {
        struct DropGuard<'a, T>(&'a mut MyLinkedList<T>);

        impl<'a, T> Drop for DropGuard<'a, T> {
            fn drop(&mut self) {
                // Continue the same loop we do below. This only runs when a destructor has
                // panicked. If another one panics this will abort.
                while self.0.pop_front_node().is_some() {}
            }
        }

        while let Some(node) = self.pop_front_node() {
            let guard = DropGuard(self);
            drop(node);
            mem::forget(guard);
        }
    }
}





fn main() {
    let mut mylist = MyLinkedList::new();
    mylist.push_back(1);
    mylist.push_back(2);
    mylist.push_back(3);

    for element in &mylist {
        println!("{}", element);
    }

    for value in mylist.iter() {
        println!("{}", value); // 输出LinkedList中的每个元素
    }

    println!("{}", mylist.contains(&2));

    for value in mylist.iter() {
        println!("{}", value); // 输出LinkedList中的每个元素
    }
}

发现比手写版的python LinkedList复杂很多。

2.2 数据结构定义

rust将abstruct api中的过程对象都具化了,抽象出2个数据结构

  • MyLinkedList,保存头结点、尾节点、长度,另外增加了一个优化点,使用marker保存当前节点。
  • MyNode,保存前向节点,后向节点,以及当前节点的值
struct MyNode<T> {
    next: Option<NonNull<MyNode<T>>>,
    prev: Option<NonNull<MyNode<T>>>,
    element: T,
}

pub struct MyLinkedList<T> {
    head: Option<NonNull<MyNode<T>>>,
    tail: Option<NonNull<MyNode<T>>>,
    len: usize,
    marker: PhantomData<Box<MyNode<T>>>,
}

通过定义,可以看到:

  • Option<NonNull<MyNode<T>>>表示一个指针引用,虽然复杂,但是语义明确且安全
    a> NonNull语义,表示如果有值,不可能为None;
    b> Option表示指向None或者Some
  • 写链表指针相关的代码,记住Option<NonNull<T>>这个语义表达。

2.3 Iterator、IntoIterator

和python一样,如果要用迭代器访问,则需要实现Iterator这个trait

trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
    …… // 很多默认方法
}

pub struct MyIter<'a, T: 'a> {
    head: Option<NonNull<MyNode<T>>>,
    tail: Option<NonNull<MyNode<T>>>,
    len: usize,
    marker: PhantomData<&'a MyNode<T>>,
}
impl<'a, T> Iterator for MyIter<'a, T> {
    type Item = &'a T;

    #[inline]
    fn next(&mut self) -> Option<&'a T> {
        if self.len == 0 {
            None
        } else {
            self.head.map(|node| unsafe {
                // Need an unbound lifetime to get 'a
                let node = &*node.as_ptr();
                self.len -= 1;
                self.head = node.next;
                &node.element
            })
        }
    }
}

如上,定义MyIter,实现MyIter的Iterator,并且在LinkedList中要实现一个iter()方法,返回MyIter。
由于rust的Move语义,我们还要定义一个MyIterMut,实现MyIterMut的Iterator,同时在LinkedList中增加一个iter_mut()方法(代码中未实现)。

为了实现 for x in list这种语义,我们需要实现IntoIterator这种trait

trait IntoIterator where Self::IntoIter: Iterator<Item=Self::Item> {
    type Item;
    type IntoIter: Iterator;
    fn into_iter(self) -> Self::IntoIter;
}

pub struct IntoIter<T> {
    list: MyLinkedList<T>,
}

impl<T> IntoIterator for MyLinkedList<T> {
    type Item = T;
    type IntoIter = IntoIter<T>;

    /// Consumes the list into an iterator yielding elements by value.
    #[inline]
    fn into_iter(self) -> IntoIter<T> {
        IntoIter { list: self }
    }
}

impl<'a, T> IntoIterator for &'a MyLinkedList<T> {
    type Item = &'a T;
    type IntoIter = MyIter<'a, T>;

    fn into_iter(self) -> MyIter<'a, T> {
        self.iter()
    }
}

impl<T> Iterator for IntoIter<T> {
    type Item = T;

    #[inline]
    fn next(&mut self) -> Option<T> {
        self.list.pop_front()
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.list.len, Some(self.list.len))
    }
}

for x in list按照rust表达有三种情况:

  • for x in list,对应impl<T> IntoIterator for MyLinkedList<T>
  • for x in &list,对应impl<'a, T> IntoIterator for &'a MyLinkedList<T>
  • for x in &mut list,对应impl<'a, T> IntoIterator for &mut 'a MyLinkedList<T>(代码中未实现)

2.4 Drop

LinkedList要实现智能指针,要实现Drop的trait

impl<T> Drop for MyLinkedList<T> {
    fn drop(&mut self) {
        struct DropGuard<'a, T>(&'a mut MyLinkedList<T>);

        impl<'a, T> Drop for DropGuard<'a, T> {
            fn drop(&mut self) {
                while self.0.pop_front_node().is_some() {}
            }
        }

        while let Some(node) = self.pop_front_node() {
            let guard = DropGuard(self);
            drop(node);
            mem::forget(guard);
        }
    }
}

2.5 手写小结

rust是一种强类型强语义语言,相比于python,要完成LinkedList的编写,除了通性上明白数据结构的本质及原理,还需要熟悉rust的特性

  • 要熟悉rust的内存管理(movecopy语义),时刻要考虑到要同时实现mut和非mut类型的操作
  • 要熟悉rust的语法糖,这些语法糖被包裹在编译器中,例如:
    a> 表达for x in list, list要实现IntoIterator,并且对于list、&list、&mut list有三种实现
    b> 表达智能指针,list要实现Drop
    c> 表达println!(“{:?}”, list),要实现Debug,并且list<T>的T也要实现Debug
    d> 表达..list,要实现Default
  • 要熟悉rust的语义表达及操作
    a> Optional,表达可选
    b> Result,表达结果
    c> NonNull,表达非空
    ...

rust新手写python代码信手拈来,但是写rust代码确困难重重,不熟悉rust的语言特性,就无法写rust代码。这就是不把rust的语法学完,就写不出来一个rust程序的原因。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容