// dictionary
never型.
never型!は、値が1つも存在しない型です1。値を作れない以上その先へ値を渡すこともできないため、!は決して完了しない計算、つまり発散する(diverge)処理の型として使われます。return式やpanic!によるパニックのように、その場で制御を別の場所へ移してしまい、値を生成しない式が!を持ちます2。
!の値は決して存在しないため、!型の式はどんな型にも型強制されます3。そのためif式やmatch式で分岐の一部だけをreturnやpanic!にしても、式全体の型が壊れません。
// 戻り値の型が!の「発散する関数」。呼び出すと戻ってきません
fn abort_order(reason: &str) -> ! {
panic!("注文を中止しました: {reason}");
}
fn charge(balance: u32, price: u32) -> u32 {
if balance >= price {
balance - price
} else {
abort_order("残高不足") // 型は!なのでu32に型強制され、if式全体はu32になります
}
}
fn main() {
println!("残り{}円", charge(3000, 1200));
}Playgroundで開く!型になる式
いずれも「値を生成せずに制御を別の場所へ移す」点が共通しています24。
| 式 | 制御の移り先 |
|---|---|
| return式 | 呼び出し元へ戻る(関数を抜ける) |
break式・continue式 |
ループの外、または次の周回へ |
panic!など実行を打ち切るマクロ |
スレッドの実行を中断する |
breakを含まないloop式 |
どこへも移らない(決して終わらない) |
戻り値が!の関数の呼び出し |
呼び出した先で発散し、戻ってこない |
補足
!を型注釈に書けるのは関数の戻り値の位置だけ(!自体はまだ不安定)
型として!を書けるのは、関数の戻り値の位置-> !に限られます1。それ以外の位置に書くと、never型が不安定な機能のままであるためコンパイルエラーになります(Rust 1.93で確認)。!は「書く型」ではなく「コンパイラが式に付ける型」として出会うのが普通です。
fn main() {
let quit: ! = panic!("注文を中止しました"); // エラー: E0658(`!`型は実験的機能)
}Playgroundで開く言語側が!型を要求する場面
let-else文のelseブロックは必ず発散しなければならず、型が!であることが求められます。マッチに失敗したまま後続へ進むと、束縛されるはずだった変数が存在しないまま参照されてしまうため、発散が言語レベルで強制されています。
ユニット型()との違い
どちらも「意味のある値を返さない」場面に現れますが、値の個数が決定的に違います。
| 型 | 値の個数 | 意味 |
|---|---|---|
ユニット型() |
1個(()のみ) |
処理は完了するが、返す値に意味がない |
! |
0個 | 処理が完了せず、値がそもそも生成されない |
値が1個ある()は「完了した」ことを伝えられますが、!は値を構築できないため、!型の式の先へ実行が進むことはありません。この違いから、!はどんな型にも型強制できる一方で、()は他の型へ型強制されません。
never型フォールバックはRust 2024で変わった
!が型強制される位置にあるのに、型推論で強制先の型を決められないことがあります。このときコンパイラが使う既定の型をnever型フォールバックと呼び、従来は()でしたが、Rust 2024エディションで!自身に変更されました5。
fn main() {
let in_stock = true;
// エラー: E0277(フォールバック先の!はDefaultを実装していない)
let _count = if in_stock { Default::default() } else { return };
}Playgroundで開く従来のエディションでは_countが()と推論されていましたが、Rust 2024では!と推論され、!がDefaultトレイトを実装していないためコンパイルエラーになります。対処はlet _count: u32 = ...のように型を明示することです。なお現在のコンパイラでは、旧エディションでもフォールバックが()であることへの依存自体がdependency_on_unit_never_type_fallbackリント(既定でdeny)のエラーになります5(Rust 1.93で確認)。
Footnotes
-
The Rust Reference: Never type —
!が値を持たない型で決して完了しない計算の結果を表すこと、!型の式が他のどんな型にも型強制されること、!が現状は関数の戻り値の位置にしか書けないことが記載されています。 ↩ ↩2 -
std: primitive type never —
break・continue・return式が!型を持つこと、panic!や終了しないloop・exitのような発散する関数も同様であることが例とともに示されています。 ↩ ↩2 -
The Rust Reference: Type coercions — 型強制の一覧に「
!to anyT」が挙げられています。 ↩ -
The Rust Reference: Loop expressions —
breakを持たないloop式、およびbreak式・continue式が発散して!型を持つことが定義されています。 ↩ -
Rust 2024 edition guide: Never type fallback change — フォールバック先が
()から!へ変わったこと、dependency_on_unit_never_type_fallbackリントによる移行検出が記載されています。 ↩ ↩2
この辞書が使われているページ
backlinks 6