Too strict orphan rules E0117
robofinch:
AFAIK the sole extra power for non-fundamental stuff — setting aside whatever reasoning goes into
unsafecode — is the ability to add new blanket impls of existing non-#[fundamental]traits for existing non-#[fundamental]types in a nonbreaking change
Adding blanket implementations is a breaking change.
Blanket implementations are defined in terms of uncovered generic parameters. A type constructor TyCons<T> covers T if it is non-fundamental, and does not if it is fundamental.
Changing a non-fundamental type constructor to a fundamental one is a breaking change, as mentioned below. (And vice-versa, too.)
PC-Killer:
impl<T> From<VecDeque<Box<Node<T>>>> for Option<Box<Node<T>>> {
Under the current orphan rules, alloc (VecDeque's crate) has the "right" to add this implementation in a non-SemVer-breaking fashion:
impl<T> From<VecDeque<T>> for Option<T> { ... }
And that overlaps with your example.
Similarly, crates today can have their own implementations following the pattern:
// These can exist in the current ecosystem.
impl<T> From<MyTypeConstructor<T>> for Option<T> { ... }
impl<T> From<MyTypeConstructor<T>> for VecDeque<T> { ... }
Changing the orphan rules to take away the "right" generally would break those crates.
Making Option<T> or VecDeque<T> fundamental would make an implementing type in the example uncovered, which is not allowed on foreign traits -- and thus would also break those crates.
It's harder to construct conundrums for your other specific examples that don't involve Option making breaking changes of its own, but not impossible. If Vec somehow moved to core,[1] the following implementation would be allowed with todays orphan rules, so long as Vec<T> didn't meet the SomeHypotheticalTrait bound.
trait SomeHypotheticalTrait {}
impl<T> SomeHypotheticalTrait for T where /* ... */;
impl<T, U> From<Option<Vec<T>>> for Option<U>
where
U: SomeHypotheticalTrait,
{ ... }
If MyVec<_> could meet the hypothetical bound, the above implementation would conflict with your first two examples.
Even if we discard such a possibility, you would need to formally define what set of circumstances allows downstream to use negative reasoning about Option's implementations. Like some argument that it's impossible for Option to write such an implementation that isn't itself a breaking change or otherwise not allowed.
(If the argument relies on coherence assuming types never change crates (like a type moving to a dependency and becoming an alias in the original crate), I doubt that would fly either.)
- like some attempts to move
io::Errortocore↩︎
Discussion in the ATmosphere