Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support traits defined in crate #1

Open
schneidersteve opened this issue Dec 23, 2020 · 5 comments
Open

Support traits defined in crate #1

schneidersteve opened this issue Dec 23, 2020 · 5 comments

Comments

@schneidersteve
Copy link

I extended the 3_inject_options_list.rs example to implement Interface4 from crate test_def.

use test_def::Interface4;

#[provides]
impl Interface4 for Comp {
    fn int4(&self) {
        println!("Interface 4");
    }
}

I get the following compiling error:

cargo run --color=always --package waiter_di --example 3_inject_options_list
   Compiling waiter_di v1.6.4 (/home/steve/workspaces/playground/rust/waiter)
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
   --> examples/3_inject_options_list.rs:116:1
    |
116 | #[provides]
    | ^^^^^^^^^^^
    | |
    | impl doesn't use only types from inside the current crate
    | `waiter_di::Container` is not defined in the current crate
    | `dyn Interface4` is not defined in the current crate
    |
    = note: define and implement a trait or new type instead
    = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to previous error

For more information about this error, try `rustc --explain E0117`.
error: could not compile `waiter_di`
@dmitryb-dev
Copy link
Owner

Hi, I mentioned similar thing in section "Factory functions". You can find more about this here. In brief, in Rust you can't implement an interface/trait from crate A for a type/struct from crate B in crate C.

The main idea, that lies behind this library - you don't need to register all components in container. To achieve this, code generator behind #[component] creates implementation of Provider<INTERFACE> trait for each interface marked by #[provides]. And this includes restrictions of Rust traits. You have next options to overcome this:

  1. put
#[provides]
impl Interface4 for Comp {

in test_def crate
2. put Interface4 in the example file.
3. Create new trait that Inherits external trait

In the real world it means that all implementations of trait must be defined in the same crate if you are using waiter library.

@schneidersteve
Copy link
Author

schneidersteve commented Dec 23, 2020

I tried 3. without success. Could you please provide a example. Even Inheritance with Traits didn't worked for me.

This restrictions makes the implementation of a Hexagonal Architecture with crates and DI not easy.

@dmitryb-dev
Copy link
Owner

It's only question of preferences. You need to keep interfaces and implementations in the same crate. If it will be the same app, you can use Rust modules instead.

@somehowchris
Copy link

I’m trying to create a small framework for handling modules and just came across this di crate which seems awesome, but this issue kinda got me thinking.

If I have a trait OnModuleInit which is like a type blueprint for a lifecycle method in a framework crate, now in the actual crate I would like to implement this, this means I have to choose one of the above methods? Is there really no way if making this work. Seems like a huge nono for me

@dmitryb-dev
Copy link
Owner

dmitryb-dev commented Apr 11, 2022

You can read about this here:
https://www.reddit.com/r/rust/comments/7agy4b/could_we_ever_implement_traits_for_structs/
https://users.rust-lang.org/t/how-do-i-implement-an-external-trait-for-an-external-struct/38623/39
https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types

I also frustrated by this. But actually the main target of DI container is to simplify the project setup and remove boilerplate code. Otherwise I don't understand why somebody would use DI library when code with DI is more verbose then without using DI, it's like "using DI just to use DI". So conciseness was "must have" when I was choosing some trade off here.

Anyway. What we can consider:

  1. To make some investigation. Maybe rust has got some new feature that will provide some workaround here. Idk right now.
  2. We can implement it in the next way. If we can't implement Provider trait for external crate struct, we can define another Provider trait in this external crate. So we will be able to create new trait like ExternalCrateProvider by using trait inheritance https://users.rust-lang.org/t/extending-traits/1802. And after that we can pass it to generator like #[provides(ExternalCrateProvider)] and use it like ExternalCrateProvider::get(&container). It's ugly and gives another limitations, this is why I didn't implemented this earlier. But probably I'll try do this is as a workaround. But as far as I remember I already tried and this didn't work or was too verbose.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants