pre-RFC: delegation by import

One thing I haven't touched on is recursive imports, e.g. use self.foo::bar; use self.bar::baz. Or, more succinctly, use self.foo.bar::{self, baz}.

I think we may want to disallow or at least discourage them via lint.

Would it be possible to delegate fields in the same manner? e.g.

pub use self.pets.cats

where cats is a field of pets.

Here, you'd be able to borrow cats without knowing about the internal structure, i.e. that it's in an internal substructure named pets. That'd be an implementation detail, not revealed in the pub API.

Yes, but as I wrote above, we may want to discourage this, as it's usually bad design to reach into inner objects.

1 Like

I don't agree with such a general statement. In the example provided self and pets might be defined in the same module. Using delegation in this way is no different that delegating a function. The point of such delegation is to present a pub API that doesn't reveal deeper structural relations than necessary.

Why? What keeps you from delegating the methods you want to use from your field and using them? With the appropriate visibility there's no publicly visible difference and you gain higher locality.

True about the better locality of reasoning. From a traditional OOP-perspective accessor methods are strongly encouraged, but they can also be considered needless ceremony in many cases.

Delegating fields can hide the internal structure while serving as a means of (or the counterpart of) "field inheritance" in Rust, where composition is the preferred style.

2 Likes

I find this an important point. Since Rust does not support inheritance, and in particular, structural inheritance, the providing a clean/easy/clear/declarative way of delegating fields AND methods would provide the best experience (Opinion obviously, but, one formed from 25+ years of development across many languages, industries, and development styles).

1 Like

This looks cool. My only concern is about expanding the use keyword for code generation cases, and if I understand this correctly, the suggestion is to generate code. So far, use has only been utilized for arranging export/import namespaces.

1 Like

The code generation is an implementation detail. You still bring method from elsewhere into this scope, albeit not from another module but from one of your fields (or receiver-only methods).

Looking at how this was solved elsewhere, here is how it's done in Lombok for Java: https://projectlombok.org/features/Delegate.html

The annotation is provided a reference to a dummy interface which it extracts the function signatures from and then implements them. This wouldn't work exactly for Rust, because implementations of a method need to be specific to which trait they are attached to as opposed to all being part of the same class declaration.

It could be done by explicitly duplicating the signatures from the trait in the call to the macro, but that is probably undesirable.

1 Like

Kotlin uses the by keyword for delegation. See documentation.

2 Likes

As a Java programmer and lombok user, I was aware of its delegation facility, though I haven't used it in my code yet.

I think the effort for this in Java is greatly reduced by the proliferation of IDEs that autocomplete function signatures. Those don't work quite as well for Rust yet, so we'd be well advised to invest in better user experience from the language itself for this case.

One thing I'm interested in discussing is potential interactions with other features, e.g. async fns, generics, lifetimes etc.

There is the ::inheritance, which in hindsight I should named delegation.

It is still fairly limited since it's "just" proc-macro-based, but with access to compiler internals it would allow to easily get something along these lines:

#[derive(DelegationImpls)]
struct MyIter<'a, T>(
    #[delegate_impl(Iterator where T : Sync)]
    std::slice::Iter<'a, T>,
);

There is already an active rfc for delegation but from what I can read so far it is not very clear to me :

  • that your future proposition would offer a different approach.
  • that you are aware this rfc exists.

In fact this rfc is partially based on an even older one of mine that already proposed to reuse the use keyword.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.