Skip to content

Proposal: Anonymous fields #9728

Closed
Closed
@orenbenkiki

Description

@orenbenkiki

This is an attempt at defining possible implementation of anonymous fields (inspired by Go). There are some "obvious" design choices and some not-so-obvious ones involved. The following is what I hope to be a "reasonable" combination. Here goes...

The idea is to allow fields without a name, e.g.:

struct Foo {
    Bar, // Anonymous field
    Baz, // Another anonymous field
    named: int // Named field
}

It is illegal to have two anonymous fields with the same type (as that would be ambiguous).

Members

Writing foo.Bar.member would access members of the Bar structure. This is always legal (since there's only one Bar member).

Writing foo.member will access a member explicitly declared in Foo, if there is one. Otherwise, of there's only one anonymous member providing the member, it is accessed. Otherwise (if there's no such anonymous field, or if there's more than one), it is an error.

Methods

Writing foo.Bar.method(...) would invoke the method on the Bar field. This is always legal (since there's only one Bar member).

Writing foo.method(...) works similarly to accessing a member - if there's a method explicitly declared for Foo, it is called; if there's only one anonymous field providing it, it is called; otherwise, it is an error.

Design choice A: When a method of an anonymous field is invoked, all self.* or function(self) accesses are interpreted using the type of the anonymous field. For example, if Foo provides a foo method, and Bar also provides a foo method, and Bar.bar invokes self.foo, then Foo.bar will invoke Bar.foo, not Foo.foo.

Traits

Design choice B: There are no implicit traits for Foo.

When implementing a trait for Foo, it is allowed to omit the implementation of a function if it is unambiguously available by any anonymous field. So, if Bar implements a trait, one should typically simply say impl Trait for Foo {} and be done.

However, if an explicit function is defined inside the impl Trait for Foo { ... } block, it would be used instead of any anonymous field function. This is consistent with the function calling semantics above. For example, it may be required to specify a function if it is provided by more than one anonymous field.

Access

Design choice C: Public/private control of members and methods accessed via the anonymous field is inherited from the type of the anonymous field. That is, Foo gets no special access rights to the internals of Bar.

Diamonds

Design choice D: If both Bar and Baz have an anonymous field called Qux, then there are two separate instances of Qux - foo.Bar.Qux... and foo.Baz.Qux.... This is consistent with reusing the compiled version of anonymous field functions, and avoids the nastiness involved with the diamond multiple-inheritance pattern. So, no equivalent to C++'s "virtual inheritance".

Alternatives

An alternative to anonymous fields is to provide a delegate syntax, allowing delegating certain methods to (named) fields. Delegation would also need to work for members. The syntax for delegation would be tricky as we don't want to repeat the whole list of methods/members on the one hand, but would need to provide some control on the other hand. Possibly delegate * to field_name would be enough, but that is essentially the same having anonymous fields.

Other issues?

The above design choices are made with an eye for a useful simple semantics combined with a simple vtable-based implementation. This doesn't make them "right" and I probably missed some issues...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions