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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM