
前回は「ありがちな」エレトステネスの篩をRustで練習してみました。しかし気になったのが mut の多さです。小さなプログラムなのに mut と宣言しているところが4か所もありました。普段、Cで書いていたら気にしないのにRustだと mut と書かねばならないのでとても気になります。Rustを作った人たちの思うツボか?
※「やっつけな日常」投稿順 Index はこちら
mutが多すぎた前回のコードを反省
前回のエラトステネスの篩のコードでの ミュータブル変数の多用を反省したコードが以下です。とりあえずループや中間変数で mut していたところを「小手先」で直してみたもの。
// Sieve of Eratosthenes 2
const N: usize = 8190;
fn sieve_of_eratosthenes() -> usize {
let mut flags: [bool; N + 1] = [true; N + 1];
let mut count: usize = 1;
print!("{} ", 2);
for i in 0..=N {
if flags[i] {
let p = i + i + 3;
print!("{} ", p);
for k in ((i+p)..=N).step_by(p) {
flags[k] = false;
}
count += 1;
}
}
return count;
}
fn main() {
println!("Sieve of Eratosthenes");
println!("----------------------------");
let count = sieve_of_eratosthenes();
println!("\n----------------------------");
println!("Number of primes < {}: {}", 2*N+3, count);
}
だいたいRustでまともにループを書いたことがないので、今回は「普通のループ」をいくつかのパターンで試してみることにしました。ループを回る回数を制御する変数と、その中で積算する変数の2つが必要になるもの。
いつもお世話になっております「Rust By Example 日本語版」様の以下のページなどを参考にさせていただいております。
mutはゼロ個の末尾再帰ループ
まずは mut を1個も書かずに済むといえば、再帰(リカージョン)でありましょう。再帰の途上の関数引数に紛れて?ループ回数にせよ、積算にせよ、あからさまな mut は必要ありませぬ。
fn rec_loop(cnt: usize) -> usize {
print!("{} ", cnt);
if cnt == 0 {
return 0;
}
cnt + rec_loop(cnt - 1)
}
一応、Rustも末尾再帰(テイル・リカージョン)をサポートしているみたいですが、どこまでどうしてくれるのかは調べてありませぬ。
forループ使えばイテレータよ
ご存じのとおり、forループでイテレータを使ってrangeを回ればループ回数の制御にあからさまな mut は不要。ただし積算用の変数には mut は必要と。ただ直にforループを回しても何なので、今回は for(int i=0; i<=cnt; i+=2)みたいなSTEPありのforループとしてみました。こんな感じ。
fn for_loop_by_2(cnt: usize) -> usize {
let mut sum = 0;
for i in (0..=cnt).step_by(2) {
print!("{} ", i);
sum += i;
}
sum
}
しかし、Pascalでいうところの downto みたいなダウンステップのforはどう書いたらよいのかしら?そういうイテレータがあるような?
whileとloopで、mutバッチリ
条件ループのwhileと無条件ループのloopでは、ループ回数の変数と、積算の変数の両方にmutつける方法しか思いつかなかったです。
まずはwhileから。ことさらに downto してます。
fn while_loop(mut cnt: usize) -> usize {
let mut sum = 0;
while cnt > 0 {
print!("{} ", cnt);
sum += cnt;
cnt -= 1;
}
sum
}
続いて無限ループのloop。ことさらに break してます。
fn cntdown_loop(mut cnt: usize) -> usize {
let mut sum = 0;
loop {
print!("{} ", cnt);
if cnt == 0 {
break;
}
sum += cnt;
cnt -= 1;
}
sum
}
どちらにせよ、変数2個に mut バッチリで芸がありませぬな。
実行結果
上記の4関数を実行してみた結果が以下に。どれも「回って」ます。
再帰であれば、mut なしでかなりイケそうな気がしないでもないです。でもデカい構造をスタックにおいて再帰すると、Rustでもスタックあふれることがあるみたいだし。当然か。

