2015年3月21日 星期六

使用Vundle 維護的新vim 設定

離上一篇「我的vim設定」已經過了一段時間
http://yodalee.blogspot.tw/2012/09/vim.html
其實這個設定已經過時,大約去年九月左右就已經整個換掉了。

現在的設定是由阿蹦大神推薦的,包括:
* Vundle: https://github.com/gmarik/Vundle.vim
自動安裝插件的插件
* ultisnips: https://github.com/SirVer/ultisnips
強大的原始碼片段展開
* vim-snippets: https://github.com/honza/vim-snippets
各種snippets的集合
* YouCompleteMe: https://github.com/Valloric/YouCompleteMe
補齊插件,包括C語言、Java(雖然我沒寫)跟python 的補齊
* Cscope: https://github.com/steffanc/cscopemaps.vim
Cscope,利用Ctags 幫助原始碼查找的工具
* vim-better-whitespace: https://github.com/ntpeters/vim-better-whitespace
vim-better-whitespace,自動幫你把trailing whitespace 給幹掉的插件

另外還有一個,不過這不是很重要(=w=)
Rust.vim: https://github.com/rust-lang/rust.vim

用上Vundle 的好處是,安裝plugin 變得簡單很多,不像以前要用dropbox同步所有設定檔,同時Vundle 可以透過github 安裝,能直接update plugin,像我剛裝的時候還沒有Rustlang 的snippets,後來update 一下就有了。

設定:

首先設定Vundle,照著Vundle 的設定打就行了,先用git 把Vundle.vim 載到~/.vim/bundle 資料
夾裡:
git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim

然後在設定檔加入
filetype off
set runtimepath+=~/.vim/bundle/Vundle.vim
call vundle#begin()
Plugin 'gmarik/Vundle.vim'
Plugin 'SirVer/ultisnips'
Plugin 'honza/vim-snippets'
Plugin 'Valloric/YouCompleteMe'
Plugin 'steffanc/cscopemaps'
Plugin 'wting/rust.vim'
Plugin 'ntpeters/vim-better-whitespace'
call vundle#end()
想要裝的plugin就像這樣,在vimrc 裡面插入Plugin,後接git repository 的url 或是author/pluginname;接著在vim 裡面下達:PluginInstall 讓Vundle 猛攪一陣就可以了。
再來是設定其他兩個plugin
首先是Ultisnips,這個比較簡單,因為我們vim-snippets 裡的檔案會存在
.vim/bundle/vim-snippets
裡面;另外是觸發snippet 的按鍵,設定Ultisnips的參數,xxxxxxx請改自己的家目錄:
let g:UltiSnipsSnippetsDir=["/home/xxxxxxx/.vim/bundle/vim-snippets/UltiSnips"]
let g:UltiSnipsExpandTrigger="<c-j>"
let g:UltiSnipsJumpForwardTrigger="<tab>"
let g:UltiSnipsJumpBackwardTrigger="<s-tab>"

youcompleteme比較麻煩,在C語言系統它要先編過一些東西,先設定vimrc:
let g:ycm_global_ycm_extra_conf = '/home/xxxxxxx/.vim/plugin/.ycm_extra_conf.py'
let g:ycm_extra_conf_vim_data = ['&filetype']
上面這個.ycm_extra_conf.py可以在.vim/bundle/YouCompleteMe/third_party/ycmd/cpp/ycm/
裡面找到(講到這裡我就要靠北一下,我記得這個py檔不設定的話YouCompleteMe會跟我一直叫叫叫,然後一堆人都遇到這個問題丟去github 上問,作者只會回「去看文件」啊你文件就沒有講吼!)
這裡面是YouCompleteMe 的設定檔,我只有把裡面的 -Wc++98-compat 改成 -Wno-c++98-compat,使用一些 C++11 的語法時才不會一直警告和 C++98不相容。

然後YouCompleteMe需要編譯,需要安裝編譯工具、Cmake跟python-dev,然後:
cd ~/.vim/bundle/YouCompleteMe
./install.sh --clang-completer

Cscope的話,首先要安裝ctags 跟cscope,在Linux 用套件管理程式都可以安裝。
接著在原始碼根目錄的地方,先用ctags -R 產生tag 檔,之後打開vim後,在關鍵字上使用Ctrl + ] 就可以進行跳躍,用Ctrl + t跳回。

大概就是這樣,現在你的Vim 應該已經變成相當強大的工具了,Happy Vim。

2015年3月1日 星期日

Rust Pointer, Ownership and Lifetime:


這篇主要參考是:
http://slides.com/liigo/rust-memory/fullscreen#/

前言:

pointer是語言上一種常見的實作方法,也是C/Cpp常見的寫法,讓你可以利用指標對資料(某塊記憶體)進行操作,達到極高的操控性。
問題是什麼呢?Pointer 讓你直接操控一塊記憶體,相對的,它也會造成空的指標,雙重釋放、未釋放記憶體等不安全(這裡的安全是指不正常的使用指標,造成memory leak)的操作;兩個指標可以指向同一塊記憶體,在多執行緖裡造成race condition(競態條件)。

我們既需要pointer 所帶來的彈性,又不希望pointer 和不正常使用帶來的不安全。

同時當系統愈來愈大,加上更多平行化機制之後,要求設計師對每塊宣告的記憶體負責愈顯得不切實際,作為下一代的語言實在不該讓程式莫名的存取到不該用的記憶體,讓作業系統丟出seg fault 把程式切掉(引用:AZ大神)。
類似的問題與回應在Cpp也看得到(我真懷疑Cpp到底有什麼概念沒實作的XDDDD),像是 C++11引入的smart pointer, shared pointer,都是針對這個問題而來。

Rust 使用的解法是:定義了Rust Ownership跟Lifetime規則,直接限制pointer 的傳遞、複製、刪除,由編譯器在編譯時即進行檢查(老實說還滿嚴格的 Jizz),避免不正當的pointer 使用方式,來兼顧安全與控制;同時排除掉執行期的檢查成本。
相對於golang,使用的是garbage collection 的方式來處理資源釋放的問題,Rust 連garbage collection 都不要有,一但你的資源(memory) 生命期結束,編譯器就自動幫你把資源釋放掉。

Pointer, Ownership, Lifetime: 

首先我們先介紹Rust 的pointer,主要有兩種型式,一種是直接的Reference,就跟C/C++一樣,可以用 & 或是 ref 取另一個變數的位址;另一種則是box pointer,跟C/C++的malloc 一樣,它會分配heap 區內的記憶體給這個pointer,使用方法請見:
Ref pointer: http://rustbyexample.com/flow_control/match/destructuring/destructure_pointers.html
Box: http://rustbyexample.com/box.html

我們這裡提到兩個名詞:持有權(Ownership)跟生命期(Lifetime),就來做細部介紹:

Ownership指的是一個pointer 對某塊記憶體的持有權。
例如在ownership guide 裡的例子,在一個scope 裡面malloc/free 一塊記憶體,在Rust 裡就是直接宣告let x = box; Rust compiler 會幫你放掉這塊記憶體。
所有的資源都只能有一個持有者,擁有持有權的pointer 可以將pointer 轉送(Move)或借給(Borrow)其他人,例如把它傳送到函數裡面,當我們把pointer 當做函數的argument時,資源擁有權就轉走了。
例如當我們用box pointer:也就是C裡面的malloc/free 型態的pointer,傳送到function 裡的時候,在caller 的變數即無法再存取這塊資料,在函式的末端,這個資源的持有權無人承接,系統就將它釋放掉,就像這樣:
fn main() {
  let x = Box::new(5);
  add_one(x);
  println!("{}", x);
}

fn add_one(mut num: Box<i32>) {
  *num += 1;
}
上面的println 會出現compile error,因為資源已經轉手給add_one,並在add_one 的末端被釋放掉,由於main裡的pointer 已經無法再存取那塊記憶體,由此杜絕存取dangling pointer 的可能性。

如果要再使用這塊記憶體,就要在函數末端將它的擁有權傳回來
fn add_one(mut num: Box<i32>) -> Box<i32> {
  *num += 1;
  num
}
這個寫法相當常見,在Rust 裡可以用ref 的寫法來代替,這在文件內稱為借用(Borrowing),就像這樣:
fn add_one(num: &mut i32) {
  *num += 1;
}
那Lifetime呢?在Rust 裡每個變數都有它的Lifetime,一般來說Rust compile 會幫你把這些都管好,你想寫明的時候才用<'name>來指明。下列兩者其實是等價,只是一個寫明Lifetime 的名字:
fn add_one<'a>(num: &'a int) -> int {
  *num + 1
}
fn add_one(num: & int) -> int {
  *num + 1
}
同時Lifetime 的範圍其實也沒那麼不好懂,大抵上就是scope,一但資源出了scope,未使用的就會被釋放掉。

結語:

Pointer, ownership, lifetime 其實是Rust 不太好搞懂的觀念,其實它就是由編譯器進行嚴格檢查,限制不當使用的C pointer,我的感想是,一但設計師適應這樣嚴格檢查,在程式設計階段自然就排除掉C/C++高自由度帶來的那些:可以但是不應該這樣寫的不安全寫法了。


參考資料:

更多內容請見
The Rust Ownership Guide
http://smallcultfollowing.com/rust-int-variations/imem-umem/guide-ownership.html
Rust by example: 17-19
http://rustbyexample.com/