はじめに
下のコードで let と let mut が使えます。
それぞれ C++ の const auto と auto に対応します。
namespace rust_like_variable_definition {
struct dummy_for_mut {};
}
#define rust_like_variable_definition_dummy_impl2(counter) rust_like_variable_definition_dummy_##counter
#define rust_like_variable_definition_dummy_impl(counter) rust_like_variable_definition_dummy_impl2(counter)
#define rust_like_variable_definition_dummy() rust_like_variable_definition_dummy_impl(__COUNTER__)
#define let const auto
#define mut rust_like_variable_definition_dummy() [[maybe_unused]] = rust_like_variable_definition::dummy_for_mut{}; auto
int main() {
let x = 1;
// x = 2; // error!
let mut y = 3;
y = 4; // ok
}
おわりに
おわりです。
おまけ
仕組みを知りたい人のために軽く説明します。
int main() {
let x = 1;
let mut y = 3;
}
このコードはマクロを展開すると以下のようになります。
int main() {
const auto x = 1;
const auto rust_like_variable_definition_dummy_0 [[maybe_unused]] = rust_like_variable_definition::dummy_for_mut{}; auto y = 3;
}
let はそのまま const auto に展開されるだけです。
問題は let mut で、これを auto に展開したいわけですが、 let が const auto に展開されるので、 mut がそれを打ち消すようなコードに展開される必要があります。
そこで、ダミー変数を作って変数定義を一度終わらせてしまいます。
そのあとに auto を置いておけば、 let mut が auto に展開されたかのように動きます。
#define mut rust_like_variable_definition_dummy() [[maybe_unused]] = rust_like_variable_definition::dummy_for_mut{}; auto
// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// この変数定義を終わらせに来た!!!(ドン!!)
ダミー変数の変数名には一意じゃないと、スコープごとに1回しか mut を使えないことになるので、困りますね。
そこで、 __COUNTER__ マクロを使って一意な識別子を生成します。
__COUNTER__ マクロは数値に展開され、その数値は展開されるたびにインクリメントされます。それを、ほかの識別子と衝突しないように長いプレフィックスと繋げて、変数名として使います。
参考: __COUNTER__マクロ - yohhoyの日記
#define rust_like_variable_definition_dummy_impl2(counter) rust_like_variable_definition_dummy_##counter
#define rust_like_variable_definition_dummy_impl(counter) rust_like_variable_definition_dummy_impl2(counter)
#define rust_like_variable_definition_dummy() rust_like_variable_definition_dummy_impl(__COUNTER__)
マクロ rust_like_variable_definition_dummy() がプレフィックス rust_like_variable_definition_dummy_ と __COUNTER__ の値を結合した識別子を返すのですが、なんか冗長に書いていますね。
これは展開された __COUNTER__ とプレフィックスを結合するためです。
ここまでしないと __COUNTER__ が展開されず rust_like_variable_definition_dummy___COUNTER__ になってしまい、連続するアンダースコアを含むため、晴れて未定義動作です。
一度関数型マクロの引数に __COUNTER__ 渡すことで __COUNTER__ が展開され、 rust_like_variable_definition_dummy_0 や rust_like_variable_definition_dummy_1 といった識別子が生成されます。
おまけのおわり
おわりです。