Rustでグローバル変数に設定データを置きたい
Rustの勉強を少し前から始めました。
Rustのプログラムで全体から参照される設定データを持つ方法を探しています。今回はサンプルですので、コマンドライン引数の0番目と1番目、つまり自分自身の名前と最初の引数を保持することにします。実用面はないかも知れませんが、サンプルということでご了承ください。
練習帳に次のようなコードを書きました。一応コンパイルは通りますが、釈然としない点がいくつかあります。
use lazy_static::lazy_static; lazy_static! { static ref argv0: String = { let args: Vec<String> = std::env::args().collect(); let s = args[0].clone(); s }; static ref argv1: String = String::new(); } fn get_str_ref() -> &'static str { &argv0 } pub(crate) fn init(args: Vec<String>) { println!("{}", get_str_ref()); // println!("{}", &argv0); }
1. init()でグローバル変数を初期化したい
色々調べたところ、Rustではグローバル変数は推奨されていないようです。確かに、野放図にグローバル変数を使うと、どこからアクセスされるかわからないし、バグの温床なのはわかります。
しかし、アプリの起動時など(設定ファイルをロードしたときとか)に値を設定して、あとは基本的に変更しない(読み込みだけ)場合はグローバル変数は便利だと思います。
今回はVec<String>
でコマンドライン引数を受け取ることにします。コンパイルを通すためにlazy_static!
の中でstd::env::args().collect()
していますが、これをしたくありません。これは例であって、呼び出し元から情報を渡したり、あるいは何らかの処理をした結果をグローバル変数に入れたいと思います。例えばアプリ名のMD5ハッシュを記録しておきたいなど。
lazy_static!
内で初期化したくない、init()
で初期化したい
わかっていないだけかも知れませんが、lazy_static!
には外部から情報を渡して初期化できないようですが、それだと使いにくいのです。外部で色々処理をして、その後にinit()
を呼んでグローバル変数を設定したほうが自由度が高く自然だと思うのですが、これはRustの理解が足りないからでしょうか。
ミュータブルの方がいい場合もある
今回は初期化後はイミュータブル(固定)でよいのですが、今後はミュータブル(値書き換え可能)なグローバル変数を作りたくなるかも知れませんので、この場を借りて同時に質問させてください。
lazy_static!
にStringを組み合わせた例が少ない
色々検索したのですが、どういうわけかlazy_static!
にStringを用いた例があまり見つかりませんでした。
2. lazy_static!
の値を参照したい
今回は単純にprintln!()
していますが、値を参照する際によくわからない問題に遭遇しました。
fn get_str_ref() -> &'static str { &argv0 }
という関数を用意した上で
pub(crate) fn init(args: Vec<String>) { println!("{}", get_str_ref()); // println!("{}", &argv0); }
すると値を表示できます。しかしコメントアウトしてあるほうは
argv0` cannot be formatted with the default formatter
とか
`argv0` doesn't implement `std::fmt::Display`
というエラーが出ます。エラーの意味はおおよそ見当が付きます(C++のoperator<<
みたいなものでたぶん独自の型でstd::fmt::Display
を実装しておけばprintln!()
できるのでしょう)が、get_str_ref()
はただリファレンスを取得して返すだけの関数です。そしてargv0
はStringなのでprintln!()
できるはずです。
この2つは何が違うのでしょうか?ただ値を表示するだけなのにリファレンスを取得する関数を書かないといけないのは不便なので原因を知りたいと思います。このリファレンス取得関数は英語の質問サイト(アドレス失念)で見つけました。
3. C++との違い
C++の問題点やRustの理念は一応本で読みました。確かにC++は野放図に使うとバグだらけで手に負えないプログラムになるので、Rustのように制約を課して危ないコードはコンパイルできないようにするのは理に適っていると思います。
しかし、グローバルの設定データのようなものは、適切にnamespaceを設定して、その中で変数名もx
とかi
みたいな外でも出てきそうな安易な名前を避ければ安全かつ簡単に扱えると思います。C++例:
namespace globalSettings { std::string programName; std::string workingDirectory; std::string userName; }
これならこの値を参照したい、あるいは書き換えたいときにglobalSettings::workingDirectory
を操作するだけです。間違って上書きすることも人並みの注意力があればしないでしょう。
Rustの所有権や借用の理念はわかりますが、このlazy_static!
については不便すぎではないかと感じていますが、たぶん私がまだRustを始めたばかりで理解が浅いのだと思います。
漠然とした質問になりますが、こういう設定データを扱う場合のRustらしい書き方はありますか?lazy_static!
を使ったこと自体がC++の考え方をひきずっているのかも知れないと思い、この質問を追加しました。
よろしくおねがいします。
回答4件
あなたの回答
tips
プレビュー