// dictionary
パス(Path).
パスは、::で区切ったセグメントを1つ以上並べて、項目・値・型・マクロ・属性を参照する書き方です1。モジュールツリーのどこに置かれた項目でも、名前をたどるパスを書けば参照できます。
パスは起点の違いで2種類に分かれます。クレートルートから始める絶対パスと、現在のモジュールから始める相対パスです2。同じ項目を、どちらの書き方でも参照できます。
mod shop {
pub mod cart {
pub fn total(prices: &[u32]) -> u32 {
prices.iter().sum()
}
}
pub mod checkout {
pub fn pay(prices: &[u32]) {
// 絶対パス: クレートルートを起点にたどる
let absolute = crate::shop::cart::total(prices);
// 相対パス: 親モジュール shop を起点にたどる
let relative = super::cart::total(prices);
assert_eq!(absolute, relative); // 同じ関数を指すので結果も同じ
println!("お支払い金額: {}円", absolute);
}
}
}
fn main() {
// 相対パス: クレートルートにいるので shop から書き始める
shop::checkout::pay(&[980, 1250]);
}Playgroundで開く| 種類 | 起点 | 先頭に書くもの |
|---|---|---|
| 絶対パス | クレートのルート | 自クレートはcrate、外部クレートはクレート名(std::io::Write) |
| 相対パス | 現在のモジュール | self、super、現在のモジュール内の名前 |
どちらを使うべきか
The Rust Programming Languageは、一般に絶対パスを選ぶことを勧めています2。項目の定義とその呼び出し側は、互いに独立して動かすことのほうが多いからです2。コードを別のモジュールへ移したときに、どちらが壊れるかは次のように分かれます。
| コードの動かし方 | 絶対パス | 相対パス |
|---|---|---|
| 呼び出し側だけを別のモジュールへ移す | そのまま使える | 書き直しが必要 |
| 定義と呼び出し側をまとめて別の場所へ移す | 書き直しが必要 | そのまま使える |
つまり「定義と呼び出しが必ず一緒に動く」と分かっているまとまりでのみ相対パスが有利で、それ以外は絶対パスのほうが移動に強い、という判断になります。前者の典型例が、対象と同じファイルに置かれて必ず一緒に動く#[cfg(test)]付きのテストモジュールで、親モジュールを指すsuperとuse宣言を組み合わせてuse super::*;と書き、親の項目を取り込むのが定番です4。
補足
関連概念: Referenceの「正規パス」
The Rust Referenceのパスの章に「絶対パス」という分類はなく、代わりに項目が定義された場所に対応するパスを正規パス(canonical path)と呼び、同じ項目を指す他のパスはすべてその別名(alias)だとしています1。
正規パスは起点ではなく定義場所で決まるため、絶対パスと同義ではありません。たとえばpub useで再エクスポートした項目をcrate::から指すパスは、絶対パスではありますが正規パスではなく別名のほうにあたります。
なお正規パスが意味を持つのは1つのクレートの中だけで、クレートをまたぐグローバルな名前空間は存在しません1。
::で始まるパス
先頭を::にしたパスはグローバルパスと呼ばれ、解決の起点がエディションによって変わります。2018エディション以降は外部プレリュード(extern prelude)から解決されるため、::の直後にはクレート名が来ます(例: ::std::time::Instant::now())。2015エディションではクレートルート、つまり2018エディション以降のcrate::にあたる場所から解決されていました1。
Footnotes
この辞書が使われているページ
backlinks 10