通常避免使用全局變量。 取而代之,儘早在某處構建對象(好比在main中),而後將對該對象的可變引用傳遞到須要它的位置。 這一般會使您的代碼更易讀。html
在決定你想要全局可變變量以前,先仔細思考。 在極少數狀況下它頗有用,因此仍是值得知道怎麼使用。git
lazy-static crate 能夠取代一些建立單例的複雜代碼。 如下是一個全局可變 vector:github
#[macro_use] extern crate lazy_static; use std::sync::Mutex; lazy_static! { static ref ARRAY: Mutex<Vec<u8>> = Mutex::new(vec![]); } fn do_a_call() { ARRAY.lock().unwrap().push(1); } fn main() { do_a_call(); do_a_call(); do_a_call(); println!("called {}", ARRAY.lock().unwrap().len()); }
然而,全局靜態變量是咱們常用的,必須找到高效的方式來建立和查找全局靜態變量。下面咱們講述如何在rust中使用全局靜態變量。算法
靜態變量在程序的整個生命週期中均可用。 它們被分配在編譯時已知的內存塊中。 所以,它們每每表明程序能夠訪問的全局狀態。 若是一個靜態變量依賴於另外一個靜態變量,那就變得特別棘手。 一些語言社區甚至談論靜態初始化順序問題的慘敗(看着你,C ++)。 其餘像C同樣,只容許使用常量/表達式進行靜態初始化,Rust也屬於這個羣體。 但有其它選擇......數組
假設咱們正在構建一個Web瀏覽器引擎。 在成千上萬要關注的事情中,咱們應該可以呈現彩色文本。 <p style =「color:blue」>應該看起來像是以藍色字體設置的段落。 但藍色是人類可讀的顏色名稱,計算機只能讀懂數字。 定義Color結構體:瀏覽器
#[derive(Clone, Debug)] pub struct Color { r: u8, g: u8, b: u8, }
咱們沒法建立靜態HashMap並初始化數據:key爲顏色名稱、value爲顏色。 首先想到,咱們可使用模式匹配來按名稱查找顏色:安全
pub fn find_color(name: &str) -> Option<Color> { match name.to_lowercase().as_str() { "amber" => Some(Color { r: 255, g: 191, b: 0 }), // hundreds of other names... "zinnwaldite brown" => Some(Color { r: 44, g: 22, b: 8 }), _ => None, } }
缺點是匹配字符串切片是一個線性遞增的搜索:擁有的顏色越多,find_color就越慢。咱們能夠建立一個靜態HashMap嗎?函數
引用:https://zhuanlan.zhihu.com/p/...post
一、當咱們想初始化一些靜態變量,這固然沒問題。例如: static AGE:u32 = 18; static NAME:&str = "hery"; static ARRAY:[u8;2] = [0x18u8, 0x11u8]; 二、有沒有想過初始化動態的數組,vector,map?結果是編譯不通, 例如: static VEC:Vec<u8> = vec![0x18u8, 0x11u8]; static MAP: HashMap<u32, String> = HashMap::new(); error: `<std::vec::Vec<T>>::new` is not yet stable as a const fn --> src\bin\u-lazy-static.rs:21:22 | 21 | static VEC:Vec<u8> = Vec::new(); | ^^^^^^^^^^ | = help: in Nightly builds, add `#![feature(const_vec_new)]` to the crate attributes to enable error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants --> src\bin\u-lazy-static.rs:22:36 | 22 | static MAP: HashMap<u32, String> = HashMap::new(); | E0015 錯誤提示: 只有 const 類型函數能被靜態或常量表達式調用。 三、我還想在使用函數初始化靜態變量,這也編譯不經過: fn mulit(i: u32) -> u32 { i * 2 } static PAGE:u32 = mulit(18);
#[macro_use] extern crate lazy_static; use std::collections::HashMap; lazy_static! { static ref VEC:Vec<u8> = vec![0x18u8, 0x11u8]; static ref MAP: HashMap<u32, String> = { let mut map = HashMap::new(); map.insert(18, "hury".to_owned()); map }; static ref PAGE:u32 = mulit(18); } fn mulit(i: u32) -> u32 { i * 2 } fn main() { println!("{:?}", *PAGE); println!("{:?}", *VEC); println!("{:?}", *MAP); }
lazy_static! 是一個容許以非平凡的方式初始化的靜態變量的包。 例如,預先計算的常規表達式,例如docopt中使用的表達式,或靜態HashMap。字體
#[macro_use] extern crate lazy_static; use std::collections::HashMap; lazy_static! { static ref COLORS_MAP: HashMap<&'static str, Color> = { let mut map = HashMap::new(); map.insert("amber", Color { r: 255, g: 191, b: 0 }); // ... map.insert("zinnwaldite brown", Color { r: 44, g: 22, b: 8 }); map }; } pub fn find_color_lazy_static(name: &str) -> Option<Color> { COLORS_MAP.get(name.to_lowercase().as_str()).cloned() }
COLORS_MAP將在首次訪問時進行初始化。 咱們如今能夠安全地將其視爲常規靜態變量。
HashMap使用有點慢的哈希算法(引用文檔)來避免DoS攻擊。 在數據量足夠大的map中有可能發生衝突。
另外一方面,phf使用完美散列(散列,保證不衝突)來構建編譯時map。 這樣咱們就能夠在運行時進行有效的恆定時間查找。
#![feature(plugin)] #![plugin(phf_macros)] #[macro_use] extern crate phf; static COLORS: phf::Map<&'static str, Color> = phf_map! { "amber" => Color { r: 255, g: 191, b: 0 }, // ... "zinnwaldite brown" => Color { r: 44, g: 22, b: 8 }, }; pub fn find_color_phf(name: &str) -> Option<Color> { COLORS.get(name.to_lowercase().as_str()).cloned() }
以上三種方式的效率對比:
#[cfg(test)] mod tests { use super::*; use test::Bencher; #[bench] fn bench_match_lookup(b: &mut Bencher) { b.iter(|| find_color("White")) } #[bench] fn bench_lazy_static_map(b: &mut Bencher) { b.iter(|| find_color_lazy_static("White")) } #[bench] fn bench_phf_map(b: &mut Bencher) { b.iter(|| find_color_phf("White")) } } $ cargo bench running 3 tests test tests::bench_lazy_static_map ... bench: 367 ns/iter (+/- 20) test tests::bench_match_lookup ... bench: 3,948 ns/iter (+/- 460) test tests::bench_phf_map ... bench: 350 ns/iter
正如預期的那樣,match語句中運行時的線性搜索是最慢的。 靜態HashMap和phf::Map都快了一個數量級,後者領先一點點。 我我的更喜歡使用phf,由於它的意圖就是建立靜態編譯時map。 lazy_static的意圖更爲通用,初始化map只是其衆多用途之一。
《how-do-i-create-a-global-mutable-singleton》https://stackoverflow.com/que...
《perfect hash fucnction》https://en.wikipedia.org/wiki...
《match-statement-efficiency》https://users.rust-lang.org/t...
《Static initializers will murder your family》https://meowni.ca/posts/stati...
《Rust Crate 使用:lazy_static》https://zhuanlan.zhihu.com/p/...