Rust 小ネタ: exec でループして FizzBuzz する
こんにちは。Fairy Devices株式会社 となんらかの関わりがある nogiro (Twitter (現 Twitter): @nogiro_iota
) です。
sat さんの exec 解説を見たんですよね。
それで、自分自身に exec したら再帰できてループできるよなと思って作成したプログラムを解説するのが今回の記事です。
個人的な趣味として変な FizzBuzz を作りたいという欲求があり、昔は GNU Make で FizzBuzz したり していました。要は、ループできれば FizzBuzz できるな!な!って記事です。(変な条件分岐するようにしたいけどあまりできていない。)
Rust でサッと書く
Rust の標準ライブラリーにある std::process::Command
を利用します。
use std::os::unix::process::CommandExt as _;
use std::process::Command;
fn main() {
let mut args = std::env::args();
let bin = args.next().unwrap();
let arg: i64 = args.next().unwrap_or("1".into()).parse().unwrap();
let s = match arg {
x if x > 100 => return,
x if x % 15 == 0 => "FizzBuzz".to_string(),
x if x % 5 == 0 => "Buzz".to_string(),
x if x % 3 == 0 => "Fizz".to_string(),
x => x.to_string(),
};
println!("{s}");
_ = Command::new(bin).args([(arg + 1).to_string()]).exec();
}
解説
std::process::Command
は子プロセスの実行などで利用するイメージですが、UNIX 系 OS だと std::os::unix::process::CommandExt
トレイトを追加で import すると exec できます。[1]
ここで、コマンドライン引数の一覧を std::env::args
で取得すると、第 1 番目には自分自身の実行可能バイナリーへのパスが入っていることに注目します。exec はパスを指定してプロセス自身をそのプログラムに置き換える命令なので、その第 1 番目 (自分自身のパス) を指定すると無限ループすることができます。
無限ループすることはできました。FizzBuzz を行うには、現在ループの何周目であるか?を知る必要があります。exec 対象として引数一覧から取得していますし偶然使ってないので、今回は第 1 引数 (std::env::args
の 2 番目) を利用してみましょう。(別の用途にも使えますし。)[2]
最初は第 1 引数を指定してないことが予想されるので、未指定の場合は 1
としましょう。そして、exec のときに 1 増やした値を第 1 引数に渡すようにしてやりましょう。そうすると「現在何周目か?」は与えてやることができました。あとはパターンマッチングするだけです。Rust だと簡単ですね。
まとめ
こんなに sys
がいっぱいの time の結果なかなか見ない。
time ./target/release/rust-sandbox-exec-fizzbuzz -10000 > /dev/null
# =>
# => real 0m3.204s
# => user 0m0.855s
# => sys 0m2.318s
(仕様上、第 1 引数に負の値を与えればループ回数を増やせるのを悪用しています。)
-
extension traits と呼ばれたりします: https://rust-lang.github.io/rfcs/0445-extension-trait-conventions.html ↩︎
-
環境変数を使っても良いでしょう。 ↩︎
Discussion