TL;DR
- ワイルドカードインポート(
use crate_name::*;)を使うと、外部クレートの破壊的でない変更でも、ローカルの名前と衝突してコードが壊れることがある。 - 自分で制御できないクレートからのワイルドカードインポートは避ける。例外は、内部モジュールの再エクスポートと
preludeである。
内容
https://effective-rust.com/wildcard.html
名前の衝突
ワイルドカードインポートした名前は優先度が低いので、通常はローカルの名前が優先される。
use bytes::*;
struct Bytes(Vec<u8>);しかし、外部クレートに追加されたメソッドの名前が、ローカルで定義したメソッドの名前と衝突することがある。たとえば、自分で次のトレイトを定義していたとする。
trait BytesLeft {
fn remaining(&self) -> usize;
}
impl BytesLeft for &[u8] {
fn remaining(&self) -> usize {
self.len()
}
}ワイルドカードでインポートしている外部クレートに、同じ名前のメソッドが追加されると、コンパイラはどちらのメソッドを呼ぶのか判断できず、コンパイルエラーになる。
let arr = [1u8, 2u8, 3u8];
let v = &arr[1..];
assert_eq!(v.remaining(), 2);自分で制御できないクレートからのワイルドカードインポートは避けるべき
例外
次の場合はワイルドカードインポートを使ってよい。
- コードを分割するためにモジュールを使っていて、内部モジュールの公開アイテムを再エクスポートする場合(
mod thing; pub use thing::*;) - 外部クレートが
preludeモジュールを提供している場合。preludeはワイルドカードでインポートされることを前提に作られている。上の問題は起こりうるが、アイテムが慎重に選ばれているので、利便性がリスクを上回る
また、ワイルドカードインポートする依存ライブラリのバージョンを固定すれば、この問題は避けられる(項目21)。
キーワード
自分の理解
どのアイテムがどのクレート由来なのか分からなくなるので、個人的にはワイルドカードインポートはしないでほしい。特に Rust は依存クレートが多くなりやすい。