waiter
waiter copied to clipboard
Support traits defined in crate
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`
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:
- 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.
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.
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.
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
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:
- To make some investigation. Maybe rust has got some new feature that will provide some workaround here. Idk right now.
- 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.