Rustのlet/const/staticの違い
Rustでコードを書いているとき,文字列リテラルや数値を変数として扱うか,定数として扱うか悩みました.
Rustではlet
変数の値は通常不変なので,定数のように扱うこともできます.一方でRustにはconst
定数やstatic
変数といったものも存在します.
それぞれどのような違いがあり,どういった用途で使い分けるのが良いのか,自分なりにまとめてみました.
let
変数
宣言した変数に値を束縛します.通常の束縛では値の所有権を保持します.
値が参照のときは,その値の借用を行います.この場合,値の所有権は保持しません.
つまり,let
で所有・借用する値の実体は,必ずプログラム中で1つしか存在しません.
// String型のインスタンスをstr1に束縛
let str1 = String::from("Hello");
// str1の持つ"Hello"の所有権をstr2に渡す
let str2 = str1;
println!("{}", str2);
// str1にアクセスすると所有権がないのでエラー
// println!("{}", str1);
// str2の参照をstr3に渡す(借用)
let str3 = &str2;
// "Hello"の所有権はstr2のままなのでエラーにならない
println!("{} {}", str2, str3);
let
変数の値は実行時に評価されます.let
変数を使用した式は実行時に計算されます.
束縛される値は通常不変ですが,mut
キーワードをつけることで可変となります.ただし値がすでにほかの変数で参照されている場合は,すべての参照が外れるまでmut
で束縛できません.
// 可変なstr4にstr2に束縛された値の所有権を渡す
let mut str4 = str2;
// str5でstr4を不変参照していると,str6で借用できない
// let str5 = &str4;
// str4への可変参照をstr6に借用する
let str6 = & mut str4;
str6.push_str("!");
println!("{}", str4);
変数は関数スコープまたはブロックスコープで宣言できます.グローバルスコープでは宣言できません.
スコープ内で同じ名前の変数を宣言しても,エラーになりません.これをシャドーイングといい,変数の再宣言以降は新しい宣言を使って式が評価されます.シャドーイングが有効な変数のスコープから抜けると,再宣言以前の変数が使用されます.
let hoge = "hoge";
{
let hoge = "fuga";
// "fuga"が表示される
println!("{}", hoge);
}
// "hoge"が表示される
println!("{}", hoge);
モジュール外に変数を公開することはできません.ただし,変数の値を返す関数をモジュール外に公開することはできます.
const
定数
定数として値を宣言します.
定数には所有権や借用といった概念はなく,const
宣言された値はコンパイル時にコードにインライン展開されます.
const HOGE: &str = "hoge";
// コンパイル時に println!("{}", "hoge"); となる
println!("{}", HOGE);
}
const
定数の値はコンパイル時に評価されます.const
定数またはconst
関数だけで構成される式は,コンパイル時に計算されます.
const fn plus_one(n: i32) -> i32 {
n + 1
}
const N: i32 = 123;
const M: i32 = plus_one(N);
// コンパイル時に println!("{}", 124); となる
println!("{}", M);
定数なので値は不変です.
const
定数はグローバルスコープを含む任意のスコープで宣言できます.また宣言した定数はモジュール外に公開することができます.
pub const EXPOSED_VALUE: &str = "Exposed";
static
変数
static
変数に束縛された値はインライン展開されず,プログラム中の1つのメモリ空間に割り当てられます.
これは複数のスレッドから見て,必ず一つの同一なメモリ空間を指します.
static hoge: &str = "hoge";
// 複数のスレッドからスレッドセーフにアクセスできる
let s = &hoge;
println!("{}", s);
static
変数の値は実行時に評価されます.
static
変数はmut
キーワードを使って可変にすることもできます.ただし可変にするとスレッドセーフではなくなるため,unsafe
ブロックで囲まないと読み書きができません.
static mut UNSAFE_VALUE: i32 = 1;
// スレッドセーフでないのでunsafeブロック以外では読み書きできない
unsafe {
UNSAFE_VALUE += 1;
println!("{}", UNSAFE_VALUE);
}
static
変数はグローバルスコープを含む任意のスコープで宣言できます.モジュール外に公開することも可能です.
私的使い分け
基本的にはlet
変数を使用し,用途によってconst
定数やstatic
変数を使おうと思います.
-
const
定数- 値をモジュール外に公開したいとき.
- プログラムやモジュール内で共有する値をグローバルスコープで宣言したいとき.
- コンパイル時に値を計算したいとき.
- 値をインライン展開したいとき.
-
static
変数- シングルトンのようにプログラム中で1つしか存在しないグローバルなオブジェクトを定義したいとき.
-
let
変数- それ以外のとき.
Discussion