#はじめに
この記事はシステムプログラミング言語であるRustのゆるい紹介記事となっています。
そのため、細かい文法や概念には触れません。興味のある方は公式のtutorialを確認してみてください。
これからRustに触れてみようかなと考えている人の背中を押せるような記事になれば幸いです。
#Rustとは
FireFoxで有名なMozilaが支援しているシステムプログラミング言語です。
6週間ごとにアップデートされており、現在の最新版は1.39.0です。
強力な型システムとリソース管理によりメモリ安全が保証されており、最近ではマイクロソフトが既存のC/C++をRustへ移行しています。
#Rustのもつ特徴
Rustにはさまざまな特徴があります。この記事ではOwnerShip(所有権)・LifeTime(生存期間)を中心として紹介します。
##所有権
Rustには所有権とよばれる機能があります。
所有権になれるまでは、ボローチェッカとの戦いを強いられることとなるでしょう。
実際のコード例とともに紹介します。コードの説明はコメントとしてコード内に埋めているので
是非一緒に確認してみてください。
fn take_vec(_: Vec<i32>){}
fn main() {
let a: Vec<i32> = vec![1,2,3]; // aは[1,2,3]という配列の所有権を持つ(所有する)
let b = a; // bにaが有している所有権を移譲する。-> aは所有権を失ったので何も入っていない状態
println!("{:?}", a); // aは何も所有していないのでerror
let c: Vec<i32> = vec![1, 2, 3];
take_vec(c); // 関数に渡しても所有権はmoveする
println!("{:?}", c);
}
Rustではそれぞれの変数が値を所有するという考え方があります。
let a = b;
のように書くと、所有権がbからaに移り、bは未初期化状態になります。
未初期化状態の変数にアクセスすることはできず、アクセスした場合はコンパイルエラーになります。
これによりuse after freeや、多重フリーを防いでいます。
一部の型(i32やu8等)は所有権の例外とされており、値がmoveするのではなくcopyされます。
fn take_vec(_: i32){}
fn main() {
let a: i32 = 100; //i32はmoveしない
let b = a;
take_vec(a);
println!("{}", a);
}
Copyトレイトが実装されているとCopyされるようになります。本記事ではトレイトについては解説をしないので、
公式の解説を参照してください。
##借用
所有権により、それぞれの変数は様々なところへmoveしてしまいます。
これだけでは不便に感じることが多いかと思われますが、Rustは借用と呼ばれる機能でこの不便さを解消しています。
借用を利用することで、一時的に値を参照することが可能となります。借用したものがスコープを外れるときは、借用を終了するだけで
借用元の値を解放することはありません。
借用元に&
をつけることで不変な借用となります。
fn take_vec(_: &Vec<i32>){}
fn main() {
let a: Vec<i32> = vec![1,2,3]; // aは[1,2,3]という配列の所有権を持つ(所有する)
let b = &a; // bはaを借用する
println!("{:?}", a); // aは借用されているだけでmoveしていない
let c: Vec<i32> = vec![1, 2, 3];
take_vec(&c); // 関数に借用を渡す
println!("{:?}", c); // 解放されていない
}
また&mut
をつけることで可変な借用になります。
fn main() {
let mut a = vec![0, 1, 2];
let b = &mut a;
b.push(3); //可変な借用なので変更可能
println!("{:?}", b);
}
不変な借用と可変な借用は共存できず、それぞれの値はいくつかの不変な借用をとるか、
たった一つの可変な借用をとるかのどちらかのみ可能です。
##ライフタイム
それぞれの変数はライフタイムとよばれるパラメータを持っています。
ライフタイムにより、スコープ終了時に自動的に解放されるのでガベージコレクションが不要となります。
fn main() {
let a = 10;
{
let b = 100;
} //ここでbはスコープが終了するので解放される
} //aが解放される
さきほど紹介した借用とライフタイムは密接に関係しています。
借用した値のライフタイムは、借用元の値より長生きしてはいけません。
もし、そういったコードを書いてしまった場合はコンパイル時に指摘されます。
fn main() {
let a = String::new();
{
let b = "aaa".to_owned();
a = &b; //ライフタイムが自分より短いものを借用してはいけない
}
println!("{}", a);
}
#さいごに
Rustはエラーがあるコードを許容しないとともに、エラーの可能性のあるコードも許容しません。
所有権等にもまれながらコードを書くうちにきれいなコードになっていくことかと思います。
最初はボローチェッカとの戦いでとてもしんどく感じるかもしれませんが、ぜひRustに触れてみてください。