Rust 单链表的实现

Posted liufu627

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Rust 单链表的实现相关的知识,希望对你有一定的参考价值。

0. 比较Windows和Ubuntu下开发Rust的区别

## Rust环境安装

> Widnows下,在 按照官方网站安装rust 后; 安装时要选windows下的工具链; 需要c++的tool-chains来编译 rust程序, 所以要安装VC++2010以上的开发环境,。

> Ubuntu下,在 按照官方网站安装rust 后(curl https://sh.rustup.rs -sSf | sh); 安装时应该安装linux下的工具链;需要linux c++的tool-chains秋编译程序,所以先使用sudo apt-get update更新系统组件,再使用sudo apt install build-essential ,在terminal中运行gcc --version正常运行就可以。

### valgrind 来检查内存泄露

> windows下不可用

> linux,mac下可用

### 编辑

都可以用VScode 和Clion来编写和运行Rust程序,Clion比vscode好用很多,有各种智能提示。

### 调试

> windows下只能使用vscode来编译rust 程序,rust工具链必须是windows-msvc,例如nightly-x86_64-pc-windows-msvc或者 stable-x86_64-pc-windows-msvc,还要安装Rust和C/C++插件,每次编译新项目时,需要重新修改lanuch.json的 type:cppvsdbg和 program:输出EXE的路径。使用vscode 调试,像段公子的六脉神剑一样,有时可以用,有时不可用,挺烦的。

>在Ubuntu下使用 Clion来编写和Debug Rust程序,当然也需要安装Rust插件;写完程序,然后再使用valgrind 来检测有没有内存泄露。Clion默认要使用gnu-toolchains, 如果安装 好了,直接就可以编译运行,不需要配置,很方便。 rust default 我默认使用nightly-x86_64-unknown-linux-gnu,有很多新特性。

以下代码都是在Ubuntu下编写和调试的。

 

1. 新建项目

> 使用cargo new  r1来创建一个可执行Rust项目,

crate-type:

--bin 默认选项; 上方命令完整形式为: cargo new r1 --bin ,输出 r1 on Linux and macos, r1.exe on windows。

--lib Normal Rustlibrary   输出 *.so files on linux, *.dylib files on osx, and *.dll files on windows.

--rlib A dynamic Rust library   输出 *.so files on linux, *.dylib files on osx, and *.dll files on windows.

--dylib A "Rust library" file;  输出 *.so files on linux, *.dylib files on osx, and *.dll files on windows.

--cdylib  A dynamic system library; 输出*.so files on Linux, *.dylib files on macOS, and *.dll files on Windows

--staticlib  A static system library; 输出 *.a archive on linux and osx and a *.lib file on windows.

--proc-macro Proc-macro library 

 

2.  在main.rs同级目录下新建一个link.rs 

 
use std::ptr::{NonNull, null};
use std::marker::PhantomData;
use std::boxed;
use std::borrow::BorrowMut;
use std::fmt::{Formatter, Error, Debug};
use core::fmt;
use std::iter::Cloned;


pub struct  LinkedInnerNode<T> {
    value: T,
    next: Option<NonNull<LinkedInnerNode<T>>>,
}

pub struct LinkedNode<T> {
    len: usize,
    root: Option<NonNull<LinkedInnerNode<T>>>,
    last: Option<NonNull<LinkedInnerNode<T>>>,
    marker: PhantomData<Box<LinkedInnerNode<T>>>,
}

impl<T> LinkedInnerNode<T>{
    pub fn getValue(&self)->&T{
        &self.value
    }
    fn new(item:T)->Self{
        LinkedInnerNode{value:item,next:None}
    }
}

impl<T: fmt::Debug>  fmt::Debug for LinkedNode<T> {
    fn fmt(&self, f: &mut fmt::Formatter<‘_>) -> fmt::Result {
        unsafe {
            let mut last_node = &self.root;
            let mut fun = || {
                while *last_node != None {
                    println!("->{:?}",   &(*last_node.unwrap().as_ptr()).value);
                    last_node =  &(*last_node.unwrap().as_ptr()).next;
                }
            };
            fun();
        }

        write!(f, "({})", "end;")
    }
}

impl<T> LinkedNode<T>  {
    pub fn  new(item:T) ->Self{
        let init_node = Some(Box::into_raw_non_null(box LinkedInnerNode::new(item)));
        LinkedNode{len:1,root: init_node,last:init_node, marker: PhantomData,}
    }

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

    pub   fn push(&mut self,item:T)->&mut Self {
        self.push_back(box LinkedInnerNode::new(item));
        self
    }

    fn push_back(&mut self, item: Box<LinkedInnerNode<T>>) ->&mut Self{
        let mut last_node=&self.root;
        let mut fun=|| {
            while *last_node != None {
                unsafe
                    {
                        if (*last_node.unwrap().as_ptr()).next == None {
                            (*last_node.unwrap().as_ptr()).next = Some(Box::into_raw_non_null(item));
                            break;
                        }
                        last_node = &(*last_node.unwrap().as_ptr()).next;
                    }
            }
        };
        fun();
        self.len +=1;
        self
    }
}

  

  

3. 修改 main.rs 

#![feature(box_into_raw_non_null)]
#![feature(box_syntax)]

use std::ops;
use std::mem::drop;
use std::marker::PhantomData;
use crate::link::LinkedNode;
use std::ptr::NonNull;

mod link;

    pub fn main() {
        let mut node=LinkedNode::new(10);
        let vc:Vec<i32>=vec![1,2,3,5];
        println!("{:?}",&vc);
        println!("{:?}",node.size());
        println!("{:?}",node.size());

        node.push(11).push(12).push(13);
        println!("{:?}",node);
    }

4.  编译并运行  

[1, 2, 3, 5]
1
1
->10
->11
->12
->13
(end;)

看起来运行良好。

5.  使用 valgrind ./r1

==9771== Memcheck, a memory error detector
==9771== Copyright (C) 2002-2017, and GNU GPL‘d, by Julian Seward et al.
==9771== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==9771== Command: ./r1
==9771==
[1, 2, 3, 5]
1
1
->10
->11
->12
->13
(end;)
==9771==
==9771== HEAP SUMMARY:
==9771== in use at exit: 64 bytes in 4 blocks
==9771== total heap usage: 24 allocs, 20 frees, 3,489 bytes allocated
==9771==
==9771== LEAK SUMMARY:
==9771== definitely lost: 16 bytes in 1 blocks
==9771== indirectly lost: 48 bytes in 3 blocks
==9771== possibly lost: 0 bytes in 0 blocks
==9771== still reachable: 0 bytes in 0 blocks
==9771== suppressed: 0 bytes in 0 blocks
==9771== Rerun with --leak-check=full to see details of leaked memory
==9771==
==9771== For lists of detected and suppressed errors, rerun with: -s
==9771== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

  发现有四个块并没有释放; 因为在unsafe块中使用 Box::into_raw_non_null来关联数据,实现上它里面有mem::forget标志离开该lifetime不释放空间。

5. 为了保证空间及时释放,可以实现drop特性,之前需要添加一个方法弹出第一个结点

fn pop_front_node(&mut self)-> Option<Box<LinkedInnerNode<T>>>  {
        let mut last_node = self.root;
        if last_node == None{
            return None;
        }

        let mut fun = || {
            unsafe {
                let node = Box::from_raw(last_node.unwrap().as_ptr());
                self.root = node.next;
                Some(node)
            }
        };
        fun()
    }

 首先将root赋予last_node,那么last_node只能在该方法有效;一旦退出,last_node将会被释放。

6. 实现drop特性

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

            impl<‘a, T> Drop for DropGuard<‘a, T> {
                fn drop(&mut self) {
                    while let Some(_) = self.0.pop_front_node() {}
                }
            }

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

  有一个DropGuard是为了避免程序因为Panic退出而没有执行到Drop,继续执行执行清理工作,避免内存泄露。

7. 使用valgrind再检测一下

==10651== Memcheck, a memory error detector
==10651== Copyright (C) 2002-2017, and GNU GPL‘d, by Julian Seward et al.
==10651== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==10651== Command: ./r1
==10651==
[1, 2, 3, 5]
1
1
->10
->11
->12
->13
(end;)
==10651==
==10651== HEAP SUMMARY:
==10651== in use at exit: 0 bytes in 0 blocks
==10651== total heap usage: 24 allocs, 24 frees, 3,489 bytes allocated
==10651==
==10651== All heap blocks were freed -- no leaks are possible
==10651==
==10651== For lists of detected and suppressed errors, rerun with: -s
==10651== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

没有泄露,很好;

参考了内库的linked_list.rs  on ~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/liballoc/collections

 

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

Rust 中的单链表

如何用c语言实现单链表的逆置?

C数据结构单链表接口函数逻辑解析与代码实现(含详细代码注释)

数据结构--单链表简单代码实现(总结)

单链表的基本实现

数据结构初阶第三篇——单链表(实现+动图演示)[建议收藏]