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 单链表的实现的主要内容,如果未能解决你的问题,请参考以下文章