Rustにおけるconstとletの違い
はじめに
この記事は詳解Rustプログラミング(ISBN978-4-7981-6022-1)の3.4.1に記述されているconst
とlet
の違いについて読み,自分の理解をまとめた記事になります.もし何か誤りなどがありましたらご指摘いただけると幸いです.
ここで提起されている疑問は,「let
はimmutable(不変)な変数宣言に使用されるけれど,それはconst
による定数の宣言とはどう異なるのか?」というものです.
まとめ
-
const
による宣言はコンパイル時定数として扱われてインライン化される.- プログラム内にハードコードされるので中身のデータは書き換えれない.
-
let
で宣言した変数について,その背後にあるデータについては可変である可能性がある.(内部可変性)- コンパイラ的には借用規則に従うことが保証できないのでふつうは許されないが,実行時に借用規則に従うことが保証できる場合に内部可変性パターンを使用した型を使用できる.
const
とlet
の雑な理解
Rustにおけるconst
は定数を宣言するためのキーワードである.const X: i64 = 5;
と書いたときはX
はi64
型の定数5を表す.
Rustにおけるlet
は変数束縛のために使われる.let x = 5;
と書いた場合にはx
は整数リテラル5に束縛される.このように書いたときにはx
はimmutableな変数束縛となる.以下のようなコードはimmutableな変数x
を変更しようとしているためコンパイルエラーになる.
fn main {
let x = 5;
x += 10;
println!("{:?}", x);
}
error[E0384]: cannot assign twice to immutable variable `x`
借用規則
Rustでは安全性を保証するために様々な制約があるが,そのひとつとして以下のような借用規則が定められている.
- 任意のタイミングで,ひとつの可変参照か不変な参照いくつでものどちらかを行える.
- 参照は常に有効でなければならない.
この規則があるために,let
で不変参照の変数x
を定義した場合にそのデータを書き換えることは通常できなくなっている.
fn main() {
let mut v = vec![1, 2, 3];
let x = &v;
v.push(4);
println!("{:?}", x);
}
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
一方で,Rustではこのような操作を許すための内部可変性と呼ばれるデザインパターンが存在している.
内部可変性
Rustにおける内部可変性とは,そのデータへの不変参照がある場合にもデータを可変化できるデザインパターンである.内部実装ではunsafe
コードを利用してRustの通常の規則を捻じ曲げているらしいが詳しいことは今回は説明しない(できない).
不変参照の中身を書き換える例として以下のようなものが考えられる.
use std::cell::RefCell;
fn main() {
let x = RefCell::new(5);
*x.borrow_mut() += 10;
println!("{:?}", x);
}
RefCell { value: 15 }
これにより,let
により定義されたimmutableな変数のデータを書き換えることができる.
参照とBox<T>
では借用規則の不変条件はコンパイル時に強制されている.一方でRefCell<T>
ではこれらの不変条件が実行時に強制される.RefCell<T>
でこれらの規則を破るとプログラムはパニックして終了する.
const
の場合にはコンパイル時定数になってプログラム中にハードコードされるため,一度定義された値を実行中に書き換えることは不可能である.
従って数学で使われる定数const
で宣言するのが良いということが改めて分かった.
感想
- コンパイル時定数の概念をもう少しちゃんと理解したい
-
const X: i64 = 2 * 5;
とかは許されるっぽい? - コンパイル時計算ちょっと調べたいですね
-
- 所有権とかの話を避けてきてしまったことは反省
- 一回LinkedList自作とかやってみると身につくらしいのでやってみたい
- 書いてから読み直したら全部参考文献に書いてあるじゃんってなった
参考文献
Discussion