Currently, static
variables are immutable, and using static mut
variables are unsafe. If we want to calculate some static
variable during runtime, we could not mark those variable as static
.
Currently, OnceCell
is used to init static
variables during runtime, but visit OnceCell
requires additional checks, which might be noisy and need extra care (why not just use static mut instead?).
Thus I want to discuss the possibility of a new syntax, #[init]
Grammar:
#[init]
use std::init; // mark `std::init` as an init function, a function could be mark for several times(different crate may mark the same function, but each init function should be only execute once.)
#[init] // this init function execute after all other #[init] procedure it marks are finished.
fn init() { // this should be a `fn()->()`
static mut str1:String=String::new();
let str2=String::from("init");
static str3:String=str2.clone();
}
fn main(){
println!("{}", unsafe {&str1}); // str1 is `static mut` since it is decleared as such in `init` function.
// println!("{}", &str2); // str2 is not static variable, thus cannot access.
println!("{}", &str3}); // str3 is `static` thus could be access directly.
}
Syntax sugar:
// no need to write #[init] here.
// fn init() { // the defination of init could be omit.
static mut str1:String=String::new();
let mut str2=String::from("init");
str2+="sugar";
static str3:String=str2.clone(); // str3 is "init sugar" now.
// }
fn main(){
println!("{}", unsafe {&str1}); // str1 is `static mut` since it is decleared as such in `init` function.
// println!("{}", &str2); // str2 is not static variable, thus cannot access.
println!("{}", &str3}); // str3 is `static` thus could be access directly.
}
#[allow(seperate_init)]
static str4:String=str2; // rustc recognize this statement as part of #[init], thus a warning should be generated.
In this case, fn main()
could be omit for really small examples:
// the following program could be recognize as part of init procedure, thus such code fragment could compiles normally.
let vec = vec![0; 5];
assert_eq!(vec, [0, 0, 0, 0, 0]);
// The following is equivalent, but potentially slower:
let mut vec = Vec::with_capacity(5);
vec.resize(5, 0);
assert_eq!(vec, [0, 0, 0, 0, 0]);
Is there any disadvantages?