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
--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
--cdylib A dynamic system library; 输出*.so files on Linux, *.dylib files on macOS, and *.dll
--staticlib A static system library; 输出 *.a archive on linux and osx and a *.lib
--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