// dictionary
型推論.
型推論(Type Inference)は、型注釈を書かなくてもコンパイラが文脈から型を決める仕組みです。Rustは静的型付け言語なのですべての値の型がコンパイル時に確定しますが、変数の宣言では型注釈を省略でき、初期値やその後の使われ方から型が決まります。
推論の材料はletの右辺だけではありません。同じ関数の本体であれば、後の行での使われ方まで見て型を決めます。次の例のstocksは、宣言した時点ではベクタ(Vec)の要素型が決まっていません。
fn main() {
let price = 500; // 整数リテラルから i32
let tax_rate = 0.1; // 小数リテラルから f64
let mut stocks = Vec::new(); // この時点では要素型が未確定
stocks.push(3_u8); // 後の行の使われ方から Vec<u8> に確定
println!("価格: {}円 / 税率: {} / 在庫: {}個", price, tax_rate, stocks[0]);
}Playgroundで開く型注釈が必須になる場面
推論が働くのは、関数の本体やconstの初期化式といった「式の内部」だけです。関数の引数のようなシグネチャ部分は呼び出し側と実装側の境界になるため、型を明示する必要があります。
| 場所 | 型注釈 | 例 |
|---|---|---|
letで宣言する変数 |
省略可 | let price = 500; |
fnの引数 |
必須 | fn tax(price: i32) |
fnの戻り値 |
推論されない(->を省略するとユニット型) |
fn tax(price: i32) -> i32 |
| 定数・静的変数 | 必須 | const TAX_RATE: f64 = 0.1; |
| 構造体のフィールド | 必須 | struct Item { price: i32 } |
関数の引数や定数のようなシグネチャ部分には、推論を頼む_(後述)も書けません。
決め手がないときの既定型
数値リテラルの型は、注釈・接尾辞・周囲の文脈から絞り込めるならその型になり、決め手がない場合に限り整数型はi32、浮動小数点型はf64が既定として採用されます1。
| リテラル | 手がかり | 決まる型 |
|---|---|---|
3 |
let x: u8 = 3; |
u8(注釈が優先) |
3 |
3_i64 |
i64(接尾辞が優先) |
3 |
なし | i32(既定) |
3.0 |
なし | f64(既定) |
補足
letでも注釈が必要になるとき
letであっても、周囲から型が絞り込めなければ注釈が必要です。次のparseは変換先の型が呼び出し側で決まるため、注釈なしではコンパイルできません。
fn main() {
let stock = "12".parse().unwrap(); // エラー: E0284(型注釈が必要)
println!("在庫: {}個", stock);
}Playgroundで開くlet stock: u32 = ...と注釈するか、"12".parse::<u32>()のように型を直接渡せば解決します。
型の一部だけを推論に任せる_
型の中に_を書くと、その部分だけをコンパイラに決めてもらえます。全体を注釈するほどではないものの、変数の型そのものは固定したいときに便利です。
fn main() {
let prices: Vec<_> = (1..=3).map(|n| n * 100).collect(); // Vecであることだけ伝え、要素型i32は推論に任せる
println!("{:?}", prices);
}Playgroundで開く推論が関数をまたがない理由
呼び出し先の実装を覗いて引数の型を決めないおかげで、シグネチャさえ変えなければ本体の書き換えが呼び出し側の型に波及せず、型エラーも原因のある関数の中に収まります。関数の引数に型注釈が必須なのは、この境界を成り立たせるためです。
Footnotes
-
The Rust Reference - Literal expressions「If the program context under-constrains the type, it defaults to the signed 32-bit integer
i32.」(浮動小数点リテラルは同様にf64) ↩
この辞書が使われているページ
backlinks 12