はじめに
先日Rustの開発環境を構築したので、理解度を深めるためにC言語っぽい感じで構造体の連結リストを作ってみました。
ところが、いざ書き始めるとコンパイルエラーが多発してしまい、Rustのルールに慣れるまで結構時間がかかるな…というのが正直な感想です。
この記事では、自分がRustを学んだときに特につまづいたポイントを備忘録としてまとめます。
C経験者がRustを学ぶ際の参考になれば幸いです。
1. 所有権(Ownership)と借用(Borrowing)
Rust最大の特徴であり、C経験者が最初にぶつかる壁かと思います。
Rustでは「値には必ず所有者が1つだけ存在する」というルールがあります。
そして所有者がスコープを抜けると、その値は自動的に破棄されます。
所有権が移動する例
Cでは問題なく動くコードでも、Rustでは所有権が移動してしまいコンパイルエラーになります。
fn main() {
let s = String::from("hello");
takes_ownership(s);
println!("{}", s); // ここでエラー:sの所有権は移動済み
}
fn takes_ownership(s: String) {
println!("{}", s);
}
借用(参照)で回避する
Rustでは所有権を渡さずに値を使いたい場合、参照を渡します。
fn main() {
let s = String::from("hello");
print_ref(&s);
println!("{}", s); // OK
}
fn print_ref(s: &String) {
println!("{}", s);
}
Cでいうポインタ渡しに近いですが、Rustでは参照のルールが厳密に管理されるようです。
2. 可変性(mut)の厳しさ
Cでは変数は基本的に可変ですが、Rustは逆で「デフォルト不変」です。
mutを忘れるケースがしばしばありました。
NGケース:
let x = 10;
x = 20; // エラー:変数宣言時にmutを付けていないので変更不可
OKケース:
let mut x = 10;
x = 20; // OK
正直、毎回mutを書くのは面倒に感じます..
しかしデフォルト不変になっているおかげで、意図しない書き換えが起きにくく、結果的にバグが減るのは確かだなと感じています。
3. 可変参照は1つだけ
Rustの借用ルールでは「可変参照は同時に1つだけ」という制約があります。
Cではこういうことができます
int x = 10;
int* a = &x;
int* b = &x; // 同じ変数を複数のポインタで参照
Rustではこれが禁止されます。
let mut x = 10;
let a = &mut x;
let b = &mut x; // エラー:可変参照は1つだけ
理由はデータ競合をコンパイル時に防ぐため。だそうです。
Rustは「可変参照は1つだけ」というルールで、競合をそもそも書けないようにしています。
組込みのようにマルチスレッドや割り込みが絡む環境では、この仕組みが特にありがたいと思います。
まとめ
Rustはまだまだ触り始めたばかりですが、Cとは違う考え方が随所にあって、早速おもしろさを感じています。
これからも気づいたことがあれば、備忘録として少しずつまとめていこうと思います。
実務でバグが発生してしまったときに、「このバグ、Rustだったら防げたな…」という視点でコードを読むと、少しだけ楽しくなるかもしれません。(実際はそんな余裕ないんですけどね)