Stream: general

Topic: Sharing code across trait implementations


RalfJ (Apr 19 2020 at 11:25, on Zulip):

We have a trait with plenty of associated types, but several (well, 2) instances use most of the same choices for that type, plus a bunch of methods depending on that type are then implemented the same way. Is there a way to share that code, without having to repeat the entire rest of the trait signature? I couldn't find one, even specialization seems not enough due to the involved assoc types (but maybe I am misunderstanding). Does anyone here have an idea? See https://github.com/rust-lang/rust/issues/71129.

Jonas Schievink (Apr 19 2020 at 11:26, on Zulip):

Can you not use assoc. type defaults?

RalfJ (Apr 19 2020 at 11:28, on Zulip):

but then default method impls cannot use those, or can they? I admit I didnt try that.

Jonas Schievink (Apr 19 2020 at 11:28, on Zulip):

No

RalfJ (Apr 19 2020 at 11:28, on Zulip):

I am reading https://github.com/rust-lang/rfcs/blob/master/text/1210-impl-specialization.md and quite confused by the add_assign example, it seems to me that default impl<T: Clone, Rhs> Add<Rhs> for T should not typecheck because Output might be different from Self.

RalfJ (Apr 19 2020 at 11:29, on Zulip):

Jonas Schievink said:

No

then it doesnt help. the types themselves aren't annoying to repeat, but the methods depending on them are.

RalfJ (Apr 19 2020 at 11:30, on Zulip):

hm, specialization as described there should actually be able to do this, but I am not sure if it is already sufficiently implemented.

Jonas Schievink (Apr 19 2020 at 11:31, on Zulip):

Specialization also doesn't allow normalizing specializable associated types

RalfJ (Apr 19 2020 at 11:33, on Zulip):

Here's some code to play with

RalfJ (Apr 19 2020 at 11:36, on Zulip):

I'd want something like this, but it doesn't work. I am not sure why though since in the specialized impl we should be able to rely on the type being i32.

Jonas Schievink (Apr 19 2020 at 11:37, on Zulip):

default impl makes all items default, so you cannot assume what the associated type normalizes to

Jonas Schievink (Apr 19 2020 at 11:38, on Zulip):

I don't think I fully understand what you're trying to do here – other is never provided an implementation, is it a default trait method in the real code?

RalfJ (Apr 19 2020 at 11:42, on Zulip):

Jonas Schievink said:

default impl makes all items default, so you cannot assume what the associated type normalizes to

ah... I want to make the items I give non-default though

RalfJ (Apr 19 2020 at 11:42, on Zulip):

so there is no way to say "this is a partial impl but some items are final"? that seems like a big gap.

RalfJ (Apr 19 2020 at 11:42, on Zulip):

Jonas Schievink said:

I don't think I fully understand what you're trying to do here – other is never provided an implementation, is it a default trait method in the real code?

this is partial code. imagine other is implemented widely differently in the final implementations.

RalfJ (Apr 19 2020 at 11:42, on Zulip):

default trait methods also exist. there's around a dozen "other" methods.

RalfJ (Apr 19 2020 at 11:43, on Zulip):

(this is the trait I am talking about, but most of what it does concretely does not matter)

RalfJ (Apr 19 2020 at 11:45, on Zulip):

Jonas Schievink said:

I don't think I fully understand what you're trying to do here – other is never provided an implementation, is it a default trait method in the real code?

here's some more code if that helps

Jonas Schievink (Apr 19 2020 at 11:49, on Zulip):

Hmm, yeah, not sure how to do this nicely...

RalfJ (Apr 19 2020 at 11:51, on Zulip):

RalfJ said:

so there is no way to say "this is a partial impl but some items are final"? that seems like a big gap.

this seems like a serious limitation. is that tracked somewhere? would the rfc1210 tracking issue be a good place to bring this up?

Jonas Schievink (Apr 19 2020 at 11:52, on Zulip):

Maybe, yeah

Hanna Kruppe (Apr 19 2020 at 12:03, on Zulip):

I only skimmed the earlier discussion but AFAICT nobody has stated the obvious yet: a low-tech way to factor out duplication in trait impls that can't be quite abstracted over with the trait system is to have macros generate the impls. This is commonly done for numeric types' impls. It has downsides and may not be worth it for just two impls, but maybe give it a shot.

RalfJ (Apr 19 2020 at 12:07, on Zulip):

ah yes... I tend to not even consider macros for abstraction/code sharing so I forgot about that approach (same recently happened with floating-point tests, where libstd also has a bunch of them and they use macros^^ I'll likely copy those tests to miri as well soon-ish)

Jonas Schievink (Apr 19 2020 at 12:08, on Zulip):

With the intermediate trait, wouldn't you only need to write a single forwarding impl?

RalfJ (Apr 19 2020 at 12:09, on Zulip):

Jonas Schievink said:

With the intermediate trait, wouldn't you only need to write a single forwarding impl?

the intermediate trait has to repeat all forwarded signatures though

RalfJ (Apr 19 2020 at 12:09, on Zulip):

in this case, that kills any wins of code sharing we might get elsewhere

Jonas Schievink (Apr 19 2020 at 12:09, on Zulip):

Yes. Only once for each method though, and then all users don't have to deal with it.

RalfJ (Apr 19 2020 at 12:10, on Zulip):

there's 2 users :rofl:

Jonas Schievink (Apr 19 2020 at 12:10, on Zulip):

Yeah, I guess it doesn't matter much which workaround you use here :D

RalfJ (Apr 19 2020 at 12:10, on Zulip):

well a base requirement is that it has to actually save LOC ;)

RalfJ (Apr 19 2020 at 12:11, on Zulip):

and the intermediate trait doesn't do that

Jonas Schievink (Apr 19 2020 at 12:11, on Zulip):

but the macro does?

RalfJ (Apr 19 2020 at 12:11, on Zulip):

I think it would, yes

Hanna Kruppe (Apr 19 2020 at 12:11, on Zulip):

Well, it only has one copy of each common signature

RalfJ (Apr 19 2020 at 12:12, on Zulip):

"it"?

Hanna Kruppe (Apr 19 2020 at 12:12, on Zulip):

The macro solution

RalfJ (Apr 19 2020 at 12:12, on Zulip):

more importantly, it has 0 copies of "forwarded" method's signatures (the ones that are not shared)

RalfJ (Apr 19 2020 at 12:13, on Zulip):

that is what killed the intermediate trait

RalfJ (Apr 19 2020 at 12:14, on Zulip):

so from all solutions so far that actually compile, I like the macro most :D

RalfJ (Apr 19 2020 at 12:14, on Zulip):

specialization would be neat but the current implementation (even the current RFC) seem too limited for that

Hanna Kruppe (Apr 19 2020 at 12:18, on Zulip):

Honestly, with only two implementations which serve different purposes, I am skeptical if merging the common code is a good idea at all. The macro approach saves LOC, but it creates extra work whenever something stops being shared by the two impls and gets less attractive the more they diverge. I don't know this code very well, but it seems both CTFE and ConstProp are only going to become more elaborate in the future, and probably in different ways.

Hanna Kruppe (Apr 19 2020 at 12:19, on Zulip):

If the duplication of signatures is the real thing bothering you, I'm almost tempted to suggest that the macro (if any) should focus on those, not supply any method bodies, and thus be applicable to all impls of Machine. But even this would probably be considered an unwise abuse of macros.

RalfJ (Apr 19 2020 at 13:08, on Zulip):

so far, of the things I plan to share, I find it really unlikely that anything in there will stop being shared between the two impls

RalfJ (Apr 19 2020 at 13:08, on Zulip):

in particular I dont think we'll ever use stacked-borrows-style ptr tagging in either of them

Charles Lew (Apr 20 2020 at 13:41, on Zulip):

https://github.com/hobofan/ambassador
There's this crate that maybe helps and maybe not.

Last update: May 29 2020 at 17:20UTC