Trait implementation variables - "impl vars"

That's fine, but you'll have to accept that without more details as to what you actually want, this is not happening at all.

Indeed. However, any progress on their part is blocked on questions about what you expect to happen in certain cases. If you cannot explain at least what might happen (not even "should") when your proposed idea is used that satisfies your own goals, I don't know how you can expect anyone else to answer them for you. Again, if this is all you do (and somehow this does get implemented), it'd be frustrating to see "but that's not what I had in mind" if there's some constraint that has to be applied during the implementation.

And yet you can't provide an example of what it looks like before and after and explain the difference? The questions here are not looking for you to answer absolutely everything, but just what you, as the idea originator, are the best person to answer.

All that said, there are a lot of effects here that this idea ends up creating that should really be hashed out up front. Of note (some of which have been asked here):

  • Does adding an "impl var" via impl Trait for T add a field to every type?
  • Can a crate change another crate's type by implementing a trait and adding a field?
  • How does this affect type layout?
  • What the heck happens with an enum?
  • Can I add fields to a ZST? Is it a ZST anymore? What happens to other crates that I depend on (and therefore cannot know about the "impl var") that assume it is (was?) a ZST?

Basically, the core question is:

  • Given a type, where does its "impl var" variables live?

If this cannot be answered, this idea is just not implementable. Of course, there are multiple possible answers for this, but they all have tradeoffs. Some off the top of my head:

Modifies the members of the struct

Pros

  • Simple in concept.

Cons

  • Does not work for types not in the crate (i.e., all top-level types in the impl line must be crate-local and non-generic).
    • Otherwise building becomes undecidable as you need to tell already-compiled crates "oh, this type has additional shadow members you need to consider". Or all types become DynSized which makes Rust removes a lot of its zero-cost abstractions.
  • Does not work for enum.
  • Does not work for ZST.

Store in the impl vtable

Pros

  • Works for all types.
  • Does not modify the "main" type.

Cons

  • Using the trait requires dyn Trait to use so that the extra data can have some per-instance place to actually live.
    • Which means that ty.uses_impl_var() is impossible and instead something like (Box::new(ty) as Box<dyn Trait>).uses_impl_var() is necessary. Note that "impl vars" would somehow have to be initialized in Box::new which means that traits which have "impl vars" need some additional information for Box to know about initializing them. Repeat for other containers which support dyn Trait in them.
    • This may mean that "impl var" is required to be const constructible (otherwise Box::new is of unknown runtime cost, not just "allocate and move")
  • Ambiguities are possible
    • Is self.impl_var a member of the type or the local impl variable of the same name?
    • Syntactic ambiguity: does accessing an "impl var" require different syntax to make it clear that the access has to happen in some other way?
    • What about super-trait "impl vars" of the same name?

As you can see, even these two (but by no means exhaustive) solutions have considerable downsides. Where you come in is helping to clarify what downsides you prefer (if they are at all acceptable in the first place). Otherwise you may be stuck with something that doesn't match what you actually want and everyone is sadder for it.

4 Likes