「Rustは安全でも難しい」といわれる理由――メモリ安全を実現する「所有権」の仕組み:基本からしっかり学ぶRust入門(5)
Rustについて基本からしっかり学んでいく本連載。今回はRustにおける核心的な機能でRustによるメモリ安全なプログラミングを実現する機能の代表となる所有権を解説します。
所有権とは?
本連載の第1回〜第4回では、Rustの基本的な文法を主にC/C++と対比させて紹介してきました。そこで取り上げなかったものに「ポインタ」があります。ポインタはC/C++では非常に便利である反面、メモリ安全性を妨げるとされています。Rustにもポインタがありますが、C/C++とは違い安全に利用できます。安全にポインタを利用できるようにしているのが「所有権」であり、「借用」です。まずは、所有権のルールを見ていきましょう。
所有権の基本的なルール
所有権とは、文字通り変数が値を所有できる権利のことです。そして、その権利を持つ変数を「所有者」といいます。Rustではある値の所有者は常に1つとなるように決められています。さらに、変数がスコープから抜けるときには、一緒に値も破棄されます。これらが所有権の基本的なルールになります。
もしこのルールから外れたコードを書いた場合は、それはコンパイラに捕捉されてエラーとなるためプログラムを実行できません。そのため所有権のルールは非常に重要と言えますが、けして難しく考える必要はありません。スカラー型などの変数と文字列型(String型)の変数を例にとり、その振る舞いを見ながら理解していきましょう。
【補足】Rustにおけるスコープ
Rustにも、プログラミングで一般的な概念としてのスコープ(scope)があります。スコープとは「範囲」という意味ですが、Rustにおいては変数の有効な範囲ということになります。すでにmain()関数をはじめとする幾つかの関数を見てきていますが、関数の内部は代表的なスコープです。またif式やwhile式などのブロックの内部もスコープになります。もっとも大きなスコープはプログラム全体です。スコープ内で宣言された変数は、そのスコープ内でのみ有効です。
スカラー型における所有権
整数型や浮動小数点型といったスカラー型の変数は、変数間の代入において所有権は基本的に複製されます。これを「所有権の複製(コピー)」といいます。変数間の代入などにおいて所有権は複製されるので、値の所有者は常に1個でなければならないというルールは守られます。この動作を確かめてみましょう。なお、この回のサンプルはownershipsパッケージに作っていきます。
let x = 1; let y = x; println!("xは{}です。", x); // 「xは1です。」 println!("yは{}です。", y); // 「yは1です。」
何の変哲もないコードです。所有権という意識も不要ではないでしょうか。まず変数xが1で初期化され、続けて変数yが変数xの値で初期化されます。変数xは値として1を所有しているので、変数yも同じく1を所有することになります。しかし、所有権が複製されるので、この2つの1は別物ということになります。
内部的な話をすると、スカラー型(この場合はi32型)である変数xも変数yも、値のためのメモリ領域はスタックと呼ばれるメモリ領域に確保され、変数yの初期化においては変数xの場所から値をコピーしてくるという動作になります。
【補足】スタックとヒープ
スタック(stack)とは、プログラムが使用する一時的なメモリ領域です。関数の内部で宣言される変数は、基本的にスタック上に領域が確保されます。スタックを使う利点は、変数用のメモリコストが低いこと、解放も自動であることです。デメリットは、スタックの大きさは固定されている場合が多く、大きなデータの置き場所には向かず、使い切ってしまうとプログラムを異常終了させるしか手段がなくなるという点です。これを解決するのがヒープ(heap)です。ヒープとは摘み草の山という意味ですが、山(ヒープ)から大きな量の草(メモリ)を持ってきたり、山に草を返したり、山の大きさを変えることができるなど、スタックに比べ融通性に富みますが、反面獲得コストは高くなります。
文字列型における所有権
文字列型(String型)も同様に見ていきましょう。String型をはじめとする非スカラー型の場合は、変数間の代入において所有権は複製でなく移動となります。これを「所有権の移動(ムーブ)」といいます。変数間の代入で所有権が移動するため、値の所有者は常に1個でなければならないというルールは守られます。しかしスカラー型とは異なった振る舞いをするようになります。これを確かめてみましょう。
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
- なぜわざわざ学習コストを払ってまでRustを採用するのか? Webエンジニア目線でRustを考察
Web開発者としての興味、関心に基づきRustを端的に紹介し、その強みや弱みについて理解を深める本連載。第1回では、Rustを採用するモチベーションとは何かを整理、考察します。 - 実装言語を「Go」から「Rust」に変更、ゲーマー向けチャットアプリ「Discord」の課題とは
ゲーマー向けチャットアプリケーション「Discord」では、基盤サービスの一つである「Read States」が十分に高速化できない問題が明らかになった。開発チームは既存のコードをさらに改善することで対応しようとした。だが、Rust言語で再実装したところ、最適化を施す以前からパフォーマンスが向上した。なぜだろうか。開発チームがその理由を語る。 - 「Rust」言語はCよりも遅いのか、研究者がベンチマーク結果を解説
ミュンヘン工科大学の研究チームのメンバーはRust言語で開発したネットワークデバイスドライバの処理速度をC言語のものと比較した。その結果、Rust版の速度低下は最大でも数%にとどまっていた。なぜ処理性能がわずかに遅くなるのか、その理由も説明した。