From 67eb3e71eb1cb89b328ad0be6378afb45edba584 Mon Sep 17 00:00:00 2001 From: Rustln Date: Mon, 11 Apr 2022 16:03:56 +0800 Subject: [PATCH 1/5] Change static variable `names` to `NAMES` --- src/advance/global-variable.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/advance/global-variable.md b/src/advance/global-variable.md index f84d83a1..a2aa91ec 100644 --- a/src/advance/global-variable.md +++ b/src/advance/global-variable.md @@ -115,10 +115,10 @@ impl Factory{ ```rust use std::sync::Mutex; -static names: Mutex = Mutex::new(String::from("Sunface, Jack, Allen")); +static NAMES: Mutex = Mutex::new(String::from("Sunface, Jack, Allen")); fn main() { - let v = names.lock().unwrap(); + let v = NAMES.lock().unwrap(); println!("{}",v); } ``` @@ -129,10 +129,10 @@ fn main() { error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants --> src/main.rs:3:42 | -3 | static names: Mutex = Mutex::new(String::from("sunface")); +3 | static NAMES: Mutex = Mutex::new(String::from("sunface")); ``` -但你又必须在声明时就对`names`进行初始化,此时就陷入了两难的境地。好在天无绝人之路,我们可以使用`lazy_static`包来解决这个问题。 +但你又必须在声明时就对`NAMES`进行初始化,此时就陷入了两难的境地。好在天无绝人之路,我们可以使用`lazy_static`包来解决这个问题。 #### lazy_static @@ -142,11 +142,11 @@ error[E0015]: calls in statics are limited to constant functions, tuple structs use std::sync::Mutex; use lazy_static::lazy_static; lazy_static! { - static ref names: Mutex = Mutex::new(String::from("Sunface, Jack, Allen")); + static ref NAMES: Mutex = Mutex::new(String::from("Sunface, Jack, Allen")); } fn main() { - let mut v = names.lock().unwrap(); + let mut v = NAMES.lock().unwrap(); v.push_str(", Myth"); println!("{}",v); } From 17b78436c29645326c4b21171e3e8ffdf40873d8 Mon Sep 17 00:00:00 2001 From: Rustln Date: Mon, 11 Apr 2022 16:26:09 +0800 Subject: [PATCH 2/5] Update global-variable.md --- src/advance/global-variable.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/advance/global-variable.md b/src/advance/global-variable.md index a2aa91ec..464c1358 100644 --- a/src/advance/global-variable.md +++ b/src/advance/global-variable.md @@ -195,27 +195,27 @@ struct Config { a: String, b: String, } -static mut config: Option<&mut Config> = None; +static mut CONFIG: Option<&mut Config> = None; fn main() { unsafe { - config = Some(&mut Config { + CONFIG = Some(&mut Config { a: "A".to_string(), b: "B".to_string(), }); - println!("{:?}",config) + println!("{:?}", CONFIG) } } ``` -以上代码我们声明了一个全局动态配置`config`,并且其值初始化为`None`,然后在程序开始运行后,给它赋予相应的值,运行后报错: +以上代码我们声明了一个全局动态配置`CONFIG`,并且其值初始化为`None`,然后在程序开始运行后,给它赋予相应的值,运行后报错: ```console error[E0716]: temporary value dropped while borrowed --> src/main.rs:10:28 | -10 | config = Some(&mut Config { +10 | CONFIG = Some(&mut Config { | _________-__________________^ | |_________| | || @@ -228,9 +228,9 @@ error[E0716]: temporary value dropped while borrowed | creates a temporary which is freed while still in use ``` -可以看到,Rust 的借用和生命周期规则限制了我们做到这一点,因为试图将一个局部生命周期的变量赋值给全局生命周期的`config`,这明显是不安全的。 +可以看到,Rust 的借用和生命周期规则限制了我们做到这一点,因为试图将一个局部生命周期的变量赋值给全局生命周期的`CONFIG`,这明显是不安全的。 -好在`Rust`为我们提供了`Box::leak`方法,它可以将一个变量从内存中泄漏(听上去怪怪的,竟然做主动内存泄漏),然后将其变为`'static`生命周期,最终该变量将和程序活得一样久,因此可以赋值给全局静态变量`config`。 +好在`Rust`为我们提供了`Box::leak`方法,它可以将一个变量从内存中泄漏(听上去怪怪的,竟然做主动内存泄漏),然后将其变为`'static`生命周期,最终该变量将和程序活得一样久,因此可以赋值给全局静态变量`CONFIG`。 ```rust use std::sync::Mutex; @@ -240,7 +240,7 @@ struct Config { a: String, b: String } -static mut config: Option<&mut Config> = None; +static mut CONFIG: Option<&mut Config> = None; fn main() { let c = Box::new(Config { @@ -250,8 +250,8 @@ fn main() { unsafe { // 将`c`从内存中泄漏,变成`'static`生命周期 - config = Some(Box::leak(c)); - println!("{:?}", config); + CONFIG = Some(Box::leak(c)); + println!("{:?}", CONFIG); } } ``` @@ -266,7 +266,7 @@ struct Config { a: String, b: String, } -static mut config: Option<&mut Config> = None; +static mut CONFIG: Option<&mut Config> = None; fn init() -> Option<&'static mut Config> { Some(&mut Config { @@ -278,9 +278,9 @@ fn init() -> Option<&'static mut Config> { fn main() { unsafe { - config = init(); + CONFIG = init(); - println!("{:?}",config) + println!("{:?}", CONFIG) } } ``` @@ -293,7 +293,7 @@ struct Config { a: String, b: String, } -static mut config: Option<&mut Config> = None; +static mut CONFIG: Option<&mut Config> = None; fn init() -> Option<&'static mut Config> { let c = Box::new(Config { @@ -307,9 +307,9 @@ fn init() -> Option<&'static mut Config> { fn main() { unsafe { - config = init(); + CONFIG = init(); - println!("{:?}",config) + println!("{:?}", CONFIG) } } ``` From b738162e810903a3cb56adeb126c984a5ba34862 Mon Sep 17 00:00:00 2001 From: Rustln Date: Mon, 11 Apr 2022 16:29:14 +0800 Subject: [PATCH 3/5] Update global-variable.md --- src/advance/global-variable.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/advance/global-variable.md b/src/advance/global-variable.md index 464c1358..622f1345 100644 --- a/src/advance/global-variable.md +++ b/src/advance/global-variable.md @@ -233,8 +233,6 @@ error[E0716]: temporary value dropped while borrowed 好在`Rust`为我们提供了`Box::leak`方法,它可以将一个变量从内存中泄漏(听上去怪怪的,竟然做主动内存泄漏),然后将其变为`'static`生命周期,最终该变量将和程序活得一样久,因此可以赋值给全局静态变量`CONFIG`。 ```rust -use std::sync::Mutex; - #[derive(Debug)] struct Config { a: String, From 5a5bc0eec971538c41436e707769835cfdfb1e42 Mon Sep 17 00:00:00 2001 From: Rustln Date: Mon, 11 Apr 2022 17:15:21 +0800 Subject: [PATCH 4/5] Add `OnceCell` document. --- src/advance/global-variable.md | 60 +++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/src/advance/global-variable.md b/src/advance/global-variable.md index 622f1345..994482d7 100644 --- a/src/advance/global-variable.md +++ b/src/advance/global-variable.md @@ -314,7 +314,65 @@ fn main() { ## 标准库中的 OnceCell -@todo +在 `Rust` 标准库中提供 `lazy::OnceCell` 和 `lazy::SyncOnceCell` 两种 `Cell`,前者用于单线程,后者用于多线程,它们用来存储堆上的信息,并且具有最多只能赋值一次的特性。 如实现一个多线程的日志组件 `Logger`: + +```rust +#![feature(once_cell)] + +use std::{lazy::SyncOnceCell, thread}; + +fn main() { + // 子线程中调用 + let handle = thread::spawn(|| { + let logger = Logger::global(); + logger.log("thread message".to_string()); + }); + + // 主线程调用 + let logger = Logger::global(); + logger.log("some message".to_string()); + + let logger2 = Logger::global(); + logger2.log("other message".to_string()); + + handle.join().unwrap(); +} + +#[derive(Debug)] +struct Logger; + +static LOGGER: SyncOnceCell = SyncOnceCell::new(); + +impl Logger { + fn global() -> &'static Logger { + // 获取或初始化 Logger + LOGGER.get_or_init(|| { + println!("Logger is being created..."); // 初始化打印 + Logger + }) + } + + fn log(&self, message: String) { + println!("{}", message) + } +} +``` + +以上代码我们声明了一个 `global()` 关联函数,并在其内部调用 `get_or_init` 进行初始化 `Logger`,之后在不同线程上多次调用 `Logger::global()` 获取其实例: + +``` + Compiling study v0.1.0 (D:\Workplaces\Rust\study) + Finished dev [unoptimized + debuginfo] target(s) in 0.63s + Running `target\debug\study.exe` +Logger is being created... +some message +other message +thread message +``` + +可以看到,`Logger is being created...` 在多个线程中使用也只被打印了一次。 + +特别注意,目前 `OnceCell` 和 `SyncOnceCell` API 暂未稳定,需启用特性 `#![feature(once_cell)]`。 ## 总结 From 022b39fe792b97f2e43eae75d2d36242493ac29b Mon Sep 17 00:00:00 2001 From: Rustln Date: Mon, 11 Apr 2022 17:29:01 +0800 Subject: [PATCH 5/5] Update global-variable.md --- src/advance/global-variable.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/advance/global-variable.md b/src/advance/global-variable.md index 994482d7..05078bbd 100644 --- a/src/advance/global-variable.md +++ b/src/advance/global-variable.md @@ -360,10 +360,7 @@ impl Logger { 以上代码我们声明了一个 `global()` 关联函数,并在其内部调用 `get_or_init` 进行初始化 `Logger`,之后在不同线程上多次调用 `Logger::global()` 获取其实例: -``` - Compiling study v0.1.0 (D:\Workplaces\Rust\study) - Finished dev [unoptimized + debuginfo] target(s) in 0.63s - Running `target\debug\study.exe` +```console Logger is being created... some message other message