Feature Request: Import outer crate as mix-in

So suppose we have a crate A which we want to publish to crates.io and we fill lib.rs with code below:

pub trait Foo {}

We might want to do something like this in another crate B:

use foo::Foo;

impl<T> Foo for Option<T> {}

but we cannot due to orphan rule.

What if we want to do it as if these two pieces are in the same crate, so we can publish crate A, meanwhile we make crate B deeply relay on crate A to a degree that crate A is like hard-coded into crate B. So it's kinda like a mix-in.

At first glance we can simply make crate A a submodule of crate B.

But as some people really hate submodule, this mix-in feature might please them.

As for the syntax, I suggest:

use(crate) foo::Foo;

impl<T> Foo for Option<T> {}

The idea is that we can import an outer crate as our private clone, if we want to expose APIs with types from this kind of crate we just reexport them in our own crate namepsace.

What do you say folks.

2 Likes

I believe this is not a good reason to add features to a language that has already spent a large majority of its complexity budget on things like borrowck/ownership, and the trait system.

I say this because from what I understand, nothing is being proposed here that isn't already possible in Rust today. That makes the feature add nothing positive, while incurring a negative in the form of making the entire ecosystem learn (and then work with) a superfluous trick.

This can never work, because the orphan rule is still in effect, and that won't change even if this proposal were accepted. The only crate that can add something like impl<T> Foo for Option<T> {} is the crate that defines the trait Foo.

Finally, private dependencies are already possible in Rust today, by not exposing any APIs from dependencies in the public API of some crate you're writing.

2 Likes

Sorry I might misled you, I meant to make a new feature. I altered the title in case it's not clear. The submodule workaround is, after all, workaround. So another way of doing it is workspace and I should mention it at first. But in case logically crate A and B might not fit into one workspace, we may need better workspace, or maybe new mentoring of how we use workspace.

Such scoped use of traits suffers from "the hash problem".

What if the trait behaved like Hash, and two different sub-crates implemented it in two different ways in their own scopes, and then shared a HashMap between them?

The two HashMaps wouldn't be the same type.

That's my interpretation of the proposal anyway. It involves embedding a complete copy of crate A into crates B and C, meaning that B::Foo and C::Foo aren't the same type. At any rate, the proposal is specifically aimed at solving the orphan problem, so I'm willing to trust that it was intended in a way that solves the orphan problem :slight_smile:

That said, this seems like a rather heavyweight feature for solving the orphan problem, and the aforementioned lack of interoperability is a downside.

I personally think the best solution for the orphan problem is supporting newtype wrappers better. That means the features we want are better features for delegation and safe-transmute (e.g. transmuting HashMap<Newtype> to HashMap<Originaltype>). And those features have a lot of applications beyond the orphan rule as well.

7 Likes

I may be misunderstanding, but isn't this a variant on OCaml's functors?