-
-
Notifications
You must be signed in to change notification settings - Fork 4
Auto Impl trait for struct Newtype(Arc<dyn Trait + Send + Sync + 'static>);
etc.?
#14
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
Comments
Interesting; the new type pattern can indeed be useful. I guess what you're asking for is automatic generation of trait methods in the new type? The first problem to solve is syntax. The first solution I can think is this:
This is not great, especially since there's no way to use #[derive] on the struct. Possibly instead using a macro to define the trait from the new type would work, but I'm skeptical (needs more thought). Would you be interested in sponsoring development? In any case I'm currently on holiday, so it'll be at least two weeks before I can get to this. |
I am not even sure what am I asking. In essence I'm just looking for projects and methods that would make writing business software in Rust easier and faster. IMO, good support for constructor-based DI would be an important piece of it.
Unfortunately I haven nothing to offer. I'm just a cheap-o OSS dev relying on free stuff, and sometimes providing my own free stuff. 🤷 I also have no experience with implementing any more complex Rust macros. All I wanted is to point out this use-case and see if it would fit under this crate's umbrella. |
Can I suggest you think about this further, in that case? I am not a fan of thinking in paradigms. OO, DI, RAII, constraints, etc all have some utility but are insufficient on their own. |
I think your request may overlap with another feature this library halfway supports: using 'impl_scope + impl_default' to produce and instantiate a new object implementing some traits. In many applications the type name of this object is irrelevant and only a single instance is constructed. KAS has a macro called make_widget (which in the master branch is built over this library) to do exactly this. Unfortunately it's somewhat specific to widgets, due to certain limitations regarding types in Rust. See also: |
I guess I'm looking for something like this: struct Email;
#[autoimpl(IFoo using self.0)]
struct OwnedEmailService(Box<EmailService>);
#[autoimpl(for<T: trait> Box<T>)]
trait EmailService {
fn send_email(&mut self, email: &Email);
} which currently complains and ideally maybe as compact as: #[autoimpl(for<T: trait + Send + Sync> struct SharedEmailService(Arc<dyn T>))]
trait EmailService {
fn send_email(&self, email: &Email);
} or #[autoimpl(for<T: trait> struct OwnedmailService(Arc<dyn T>))]
trait EmailService {
fn send_email(&mut self, email: &Email);
} dependening on who is responsible for thread-safety. If take to extreme, since this is supposed to be used commonly in bussiness code to support D.I.: #[di(OwnedmailService, Box)]
trait EmailService {
fn send_email(&mut self, email: &Email);
} even, though I know I'm now stretching it, just to save some keystrokes. This is specifically to abstract away the implementation of but other than this I used this a lot in my own code, by making |
So, from your first code, I presume you put the wrong trait name: #[autoimpl(EmailService using self.0)]
struct OwnedEmailService(Box<EmailService>); This is very close to #[autoimpl(for<T: trait + Send + Sync> struct SharedEmailService(Arc<dyn T>))]
trait EmailService {
fn send_email(&self, email: &Email);
} This works around the problem: the trait is now part of the macro input, hence this could be supported. It's clunky syntax ( #[di(OwnedmailService, Box)]
trait EmailService {
fn send_email(&mut self, email: &Email);
} I'm not sure how to read this one — that Certainly this is neater syntax, but very "magical": hard to understand how it works without a manual (although to some extend this is true of the above macros too). ConclusionSomething like this is possible, but we should think about syntax some more. The middle option is the most viable, but leaves a few questions unanswered:
#[autoimpl(for<T: trait> &T, &mut T, Box<T>)]
#[autoimpl(struct SharedEmailService<T: trait + Send + Sync>(pub Arc<T>))]
trait EmailService {
fn send_email(&self, email: &Email);
}
// or just a non-parameterised version:
#[autoimpl(struct SharedEmailService(pub Arc<dyn trait>))]
trait EmailService { .. } |
Other than this agreed on everything, AFAICT. |
Oh, then I completely mis-read |
Here's another couple of possibilities. Yes, they aren't completely minimal. Thoughts? Autoimpl on trait definitions with using syntax#[autoimpl(for<T: trait> &T, &mut T, Box<T>)]
// Here, support `using self.foo` syntax:
#[autoimpl(for<T: trait> SharedEmailService<T> using self.0)]
pub trait EmailService {
fn send_email(&self, email: &Email);
}
pub struct SharedEmailService<T: EmailService + Send + Sync>(pub Arc<T>); Autoimpl on type definitions with custom generic parameters// Since SharedEmailService<T> supports Deref to some T: trait, we can implement just like for Box<T>
#[autoimpl(for<T: trait> &T, &mut T, Box<T>, SharedEmailService<T>)]
pub trait EmailService {
fn send_email(&self, email: &Email);
}
// Here, we support optional parameter `Deref<Target = ...>`
// (if not specified, this defaults to the field type, which would be Arc<T> here)
#[autoimpl(Deref<Target = T> using self.0)]
pub struct SharedEmailService<T: EmailService + Send + Sync>(pub Arc<T>); |
BTW. In a project I have an opportunity to try this pattern (which we call "dyn newtype"), we went with a This is what I wish I could achieve without having to maintain my own macro. |
I had a look at your macro: basically, implement #[autoimpl(for<I: IDatabase + Send + Sync + 'static> From<I> with |i| Self(Arc::new(i)))] ... but it's hardly better than writing the impl by hand: impl<I: IDatabase + Send + Sync + 'static> From<I> for Database {
fn from(i: I) -> Self {
Self(Arc::new(i))
}
} The I guess what you're asking for is something more like this: #[derive(Clone)]
#[impl_newtype(Deref, DerefMut, From)]
pub struct Database(Arc<dyn IDatabase + Send + Sync + 'static>); The catch is it would be quite specific, supporting only structs with a single field like ... but e.g. it should also support I could write such a thing. Please send an email if you wish to motivate me. |
I also have bunch of other macros for other things (clone, eq and some more custom stuff), but this is the core one that a type starts with.
I'm not convinced myself if this is something this library has to support. I just think it's worth considering. As things are right now, working with |
Yes it is. Sometimes restricting methods with
My approach is usually to skip the newtype (maybe use a typedef instead). |
Nice. I'll give it a try in some short/mid term and report back. Thanks! |
Sure. For the most part it's not implementing the trait directly on the newtype, but it still works (maybe unless you want to pass the newtype into a function with a bound on the trait?). Do let me know if there are issues. It is possible to directly implement the trait on the newtype using this, but I think only for traits with a type parameter (like |
I'm researching best approach for writing business app code like typically done in JVM languages etc. I'm not a fan of OOP, and I really dislike SpringBoot style automagic DI. However abstracting away external components behind an interface, and constructor-passing DI are really great for larger apps.
In the past when doing trait as an interface for multiple implementations for DI, I would go for
type SharedConnection = Arc<dyn Connection + Send + Sync + 'static>)
kind of thing, and I'm happy to finally find a crate that can get rid of some of that boilerplate.However not long ago, I've read https://jmmv.dev/2022/04/rust-traits-and-dependency-injection.html#good-solution-newtype, and I think a NewType is even better, it's just I never wanted to put an effort writting out all that stuff.
Would supporting this pattern fall under the goals of this crate?
Going even further, I wonder if supporting things like
struct Newtype(Arc<RwLock<dyn Trait + Send + Sync + 'static>>)
would be possible.The text was updated successfully, but these errors were encountered: