孤児ルール
何が、ユーザーが任意の型に対して任意のトレイト実装を書けないようにしているのでしょうか?
// Copyright 2025 Google LLC // SPDX-License-Identifier: Apache-2.0 // クレート `postgresql-bindings` pub struct PostgresqlConn(/* 詳細 */); // `postgresql-bindings` に依存するクレート `database-traits` pub trait DbConnection { /* メソッド */ } impl DbConnection for PostgresqlConn {} // ✅, `DbConnection` はローカルです。 // `database-traits` に依存するクレート `mycoolnewdb` pub struct MyCoolNewDbConn(/* 詳細 */); impl DbConnection for MyCoolNewDbConn {} // ✅, `MyCoolNewDbConn` はローカルです。 // `PostgresqlConn` と `DbConnection` はどちらも `mycoolnewdb` に対してローカルではありません。 // これでは `PostgresqlConn` に対する `DbConnection` の実装が 2 つになってしまいます! impl DbConnection for PostgresqlConn {} // ❌🔨
-
Rust のエコシステムでは、トレイトが二重に実装可能であってはなりません。同じ型に対する同じトレイトの 2 つの実装は、解決策のない競合です。
-
1 つのクレート内であれば、複数の定義を検出して禁止することでこれを防げます。しかし、Rust エコシステム全体にまたがるクレート間ではどうでしょうか?
-
型は、そのクレート内で定義されている、つまりクレートに対して ローカル であるか、そうでないかのどちらかです。
この例の「クレート」では、
PostgresqlConnはpostgresql-bindingsに対してローカルであり、MyCoolNewDbConnはmycoolnewdbに対してローカルです。 -
トレイトも同様に、そのクレート内で定義されている、つまりクレートに対して ローカル であるか、そうでないかのどちらかです。
同じくこの例では、
DbConnectionトレイトはdatabase-traitsに対してローカルです。 -
何かがローカルであれば、それに対するトレイト実装を書けます。
トレイトがローカルであれば、そのトレイトの実装を任意の型に対して書けます。
型がローカルであれば、その型に対する任意のトレイト実装を書けます。
-
これらの境界の外では、トレイト実装を書くことはできません。
これにより実装の「コヒーレンス」が保たれます。つまり、ある型に対するあるトレイトの実装は、クレートをまたいでも 1 つしか存在できません。
参考:
- https://doc.rust-lang.org/stable/reference/items/implementations.html#r-items.impl.trait.orphan-rule