Ixrec / rust-orphan-rules

An unofficial, experimental place for documenting and gathering feedback on the design problems around Rust's orphan rules
Apache License 2.0
200 stars 3 forks source link

Allow trait/type-providing crates to "bless" other crates for orphan impling #11

Open Ixrec opened 6 years ago

Ixrec commented 6 years ago

In other words, solve the the diesel-chrono problem by making both diesel and chrono add something like allowed-orphan-implers = ["diesel-chrono"] to their Cargo.tomls (the precise mechanism isn't important), and then a diesel-chrono crate that impls diesel traits for chrono types will no longer fail to compile.

To me, this seems like the only viable solution for "official orphans" use cases aside from giving up and embracing incoherence (because I don't think https://github.com/Ixrec/rust-orphan-rules/issues/16 or https://github.com/Ixrec/rust-orphan-rules/issues/12 are viable/actually solve the problem).

Specific "blessing mechanisms" that have been proposed include:

gbutler69 commented 6 years ago

I realize I should've added this to this Issue instead of a comment here:

One possibility for "solving" this diesel-chrono type of issue is the notion of "friend" crates.

Overview

What does it look like?


// Crate t containing Trait T

// (nothing new/special required)

// Crate u containing trait U

// (nothing new/special required)

// Crate s providing structs that need impls for u::U and t::T

// main.rs/lib.rs
// (nothing new/special required)

// Make the 'baz' module's private (no visibility spec) (not pub(self), pub(nofriend), pub(super), pub(in ...mod)) items publicized to friends
pub(friend) mod baz;

...

// foo.rs (module foo)

// if nothing is designated as pub(friend) then the "friend" visible items are the same as the "pub" visible items 
// no changes to this module in that case

// foo/bar.rs (module foo::bar)

// even though this module isn't pub(friend) items within it (including structs, enums, Trait impls, fns, Inheren Methods, and sub-modules) can be pub(friend)

pub(friend) struct FooBarYep {
  // struct private elements are made "friend" accessible too
}

pub(friend) impl for FooBarYep {
  ...
}

pub(friend) fn FooBarYepProcessor( p : FooBarYep ) {

}

// not accessible to friends
struct FooBarNope {

}

// ...etc...

// baz.rs (module baz)

// nothing special needed here as the entire 'baz' module has been publicized to friends, so, if anything needs special access by a friend, it has it

// this is accessible to friends
struct Bazup {
  // all private/pub members of this accessible to friends
}

// however, if something should be "true private" (even hidden from friends, it can be declared as "pub(self)" or "pub(nofriend)"

// not accessible to friends
pub(self) struct Bazzit {
}

pub(nofriend) struct Bazout {
}

// accessible to friends 
struct Bazme {
    x : i32; // accessible to friends (because the mod has been "friended")
    pub(self) y: i32; // not accessible to friends, only accessible within the Baz mod
    pub(nofriend) z: i32; // not accessible to friends, true private even though the module was friended
    pub t : i32; // also accessible to friends
    pub(super) u : i32; // not accessible to friends
    pub(friend) v : i32; // accessible to friends (lint warning for unneeded pub(friend)
    pub(self,friend) v : 32; // only accessible to self and friends
}

// accessible to friends, but, has a lint warning for unneeded pub(friend)
pub(friend) Bazyou {
  ...
}

// main-friends.rs/lib-friends.rs (declare friend crates)

friend friend-t-impl-s-Sy impl (::t::T) for ::crate::{Baz,Foo::Bar} // provide impls of Trait T of crate t by friend crate friend-t-impl-s-Sy
friend friend-t-impl-s-Sy impl (::t::T2) for ::crate::{Baz,Foo::Bar} // provide impls of Trait T2 of crate t by friend crate friend-t-impl-s-Sy

//// friend friend-t-impl-s-Sy impl (::t::T,::t::T2) for ::crate::{Baz,Foo::Bar}

friend friend-u-impl-s-Sy impl (::u::U) for ::crate::{Baz,Foo::Bar} // provide impls of Trait U of crate u by friend crate friend-u-impl-s-Sy

// Crate friend-t-impl-s-Sy containing impls for Trait T from t for types Sy subset(a) of Sx of crate s 

use ::t::T;
use ::u::U;

use ::s::friend::Baz;
use ::s::friend::Foo::Bar;

pub timpl {
  impl T for Baz::Bazup {
  ...
  }

  impl T for Bar::FooBarYep {
  ...
  }

  // Not permitted
  //impl T for Bar::FooBarNope {
  // ...
  //}
}

// Crate friend-u-impl-s-Sz containing impls for Trait U from u for types Sz subset(b) of Sx of crate s 

// similar for u::U as for t::T above

// Crate u that uses Sy from s and wants to use the impls of T for Sy from s provided by friend-t-impl-s-Sy and for Sz from s provided by frient-t-impl-s-Sz

use ::t::T;
use ::u::U;
use ::s::Bar::FooBarYep;
use ::friend-t-impl-s-Sy::timpl;

// have impl of T for FooBarYep provided by friend-t-imp-s-Sy crate here

How do we teach this?

What does this give us?

What DOESN'T this give us?

What are the problems/uncertainties?

How would the compiler implement this?

How would cargo implement this?

Alternatives