はじめに
プログラミング言語の設計において「シンプルさ」は永遠のテーマです。新しい機能を追加すれば便利になるが、同時に複雑さも増します。この矛盾にRustはどう向き合っているのでしょうか?
今回は、Chad Nauseam氏による記事「Keep Rust Simple」を中心に紹介します。
Rustが持たない10の機能
Rustには、多くの言語が「便利」とする以下の機能がありません:
- 名前付き引数(Named arguments)
- デフォルト引数(Default arguments)
- 三項演算子(Ternary operator)
- null
- 例外(Exceptions)
- 関数のオーバーロード(Function overloading)
- 継承(Inheritance)
- コンストラクタ(C++的な意味での)
- 可変長引数関数(Variadic functions)
- 暗黙的な型変換(Implicit type conversions)
これらの機能がなくても、実際には何でもできます。これらは単なる「便利機能」であり、これらの便利機能がないことで言語がよりシンプルになります。学習しやすく、使いやすくなるのです。
「便利さのための複雑さ」vs「パワーのための複雑さ」
しかし、Rustが完全にシンプルかというと、そうではありません。例えば文字列型を見てみましょう:
-
&str
:「文字列参照」 -
String
:「所有された文字列」
もっと易しい言語から来た人には複雑に見えるかもしれません。なぜすべてをString
にしないのでしょうか?
これが 「便利さのために追加された複雑さではなく、パワーのために交換された複雑さ」 だと主張しています。
fn get_first_word(s: &str) -> &str {
s.split_whitespace().next().unwrap_or("")
}
&str
があることで、「文字列の一部」への参照を、不要なコピーを作ることなく取得できます。これは実用的なパワーを提供する複雑さなのです。
静的解析によるシンプルさの実現
Rustのシンプルさの真価は、静的解析による間違いの防止にあります。以下のPythonコードを見てください:
# Python - 間違いを見つけるのが困難
x = [2, 1, 4]
x = x.sort() # これはNoneを返すため、xはNoneになってしまう
print(x)
同じことをRustで書くとどうなるでしょうか:
fn test() {
let x = vec![2,1,3];
let x = x.sort();
// error: cannot borrow `x` as mutable, as it is not declared as mutable
println!("{x:?}")
}
さらに、変数を可変にしても:
fn test() {
let mut x = vec![2,1,3];
let x = x.sort();
println!("{x:?}")
}
Rustは正確に何が間違っているかを教えてくれます:
warning: this let-binding has unit value
--> src/lib.rs:3:5
|
3 | let x = x.sort();
| ^^^^^^^^^^^^^^^^^
help: omit the `let` binding and replace variable usages with `()`
|
3 ~ x.sort();
4 ~ println!("{():?}")
間違いを検出して説明してくれる強力な静的解析は、実は初心者にとって非常に便利なのです(多くの間違いを犯しがちな初心者にとって特に)。
表現式としてのif:三項演算子の代替
Rustには三項演算子がありませんが、実際には必要ありません。なぜならif
が式だからです:
let result = if condition { value_a } else { value_b };
これは三項演算子(condition ? value_a : value_b
)と同じことができますが、新しい概念を言語に追加する必要がありません。
「機能の追加」より「制限の解除」
記事はRustの「シンプルな」側面を紹介していますが、Rustは比較的新しい言語で、次々に新たな機能が追加されています。ではシンプルさが失われつつあるかというとそうではありません。
十分に成熟した現代の言語設計では、「機能を追加する」よりも「制限を解除する」ことのほうが重要です:
- 新しく覚えることを追加する
ではなく
- 覚えることを減らす
ような言語変更です。
例えば:
- traitsでasyncが使えるようになった
- 借用チェッカーがPoloniusによってより寛容になる
これらは「新機能」ですが、学習者にとっては「当然できるはず」と感じられ、学習コストを下げることにつながります。
まとめ
「Keep Rust Simple」:
- 本当に必要な複雑さと、単なる便利さのための複雑さを区別すること
- 「機能の追加」よりも「制限の解除」を重視すること
- 静的解析によって間違いを早期発見すること
「シンプル」は目的ではなく、より良いプログラムを書くための手段なのです。