Why can't structs inherit from other structs?


(Rick M) #1

It sure seems natural.

Is there some reason the language can't allow a sub-struct to add member variables, such that the whole is treated like a contiguous set of members?

In my case, I have a rect-like value type, but I'd rather it inherit from CGRect, rather than contain a CGRect. That makes it much more natural to use.

Is allowing that just too complicated, or does it create type safety issues?

···

--
Rick Mann
rmann@latencyzero.com


(Jens Alfke) #2

I’m pretty sure one of the reasons is because inheritance implies method inheritance, which implies being able to override methods, and that opens a big can of worms which will be familiar to any experienced C++ programmers.

Swift struct methods aren’t dynamically dispatched; they’re just function calls. But if you could subclass a struct and override methods, then the methods would need to be dynamically dispatched … but that implies that structs would need to contain vtables (or isa pointers), which makes them a lot more heavyweight. Or on the other hand, if struct methods stayed statically dispatched, then overriding them would be fraught with peril, for the same reason that overriding nonvirtual methods in C++ is generally a very bad idea.

—Jens


(Dave Reed) #3

Inheritance isn't really compatible with value types at least if they're allocated on a stack (known as "stack-dynamic) and take a fixed amount of space (think about what would happen if you assigned it to a subclass that had additional data - no room to store the additional data). References are typically stored on a heap with a pointer to the object stored on a stack (pointers takes the same amount of memory no matter what type they point to).

If you don't need extra data (additional instance variables), you can add new methods to CGRect using extensions.

HTH,
Dave Reed

···

On Aug 1, 2016, at 8:28 PM, Rick Mann via swift-users <swift-users@swift.org> wrote:

It sure seems natural.

Is there some reason the language can't allow a sub-struct to add member variables, such that the whole is treated like a contiguous set of members?

In my case, I have a rect-like value type, but I'd rather it inherit from CGRect, rather than contain a CGRect. That makes it much more natural to use.

Is allowing that just too complicated, or does it create type safety issues?

--
Rick Mann
rmann@latencyzero.com


(Brent Royal-Gordon) #4

It sure seems natural.

Is there some reason the language can't allow a sub-struct to add member variables, such that the whole is treated like a contiguous set of members?

Structs are very "raw":

* There is no extra overhead for metadata; objects have a header which includes a type reference, a reference count, and various other housekeeping bits.

* Structs have a fixed size; there's no space for additional fields.

* Structs have statically-dispatched methods and properties; there's no ability to override.

This makes them small and fast, which are necessities if we're to use them to implement fundamental data types like `Int`. Basically, if structs weren't already so "primitive",

In my case, I have a rect-like value type, but I'd rather it inherit from CGRect, rather than contain a CGRect. That makes it much more natural to use.

Keep in mind that you have three options—not just one—if you want to increase the capabilities of a struct:

  1. Composition, as you seem to have already evaluated.

  2. Extensions to add extra methods or computed properties to an existing struct.

  3. Protocols and retroactive modeling if you want to be able to handle instances of both types with a single piece of code.

You don't mention what it is you want to do, but it's possible that one of these will address your needs.

···

On Aug 1, 2016, at 5:28 PM, Rick Mann via swift-users <swift-users@swift.org> wrote:

--
Brent Royal-Gordon
Architechies


(Tino) #5

There is no fundamental reason to disallow struct inheritance, and I started a pitch where afair one member of core agreed it would be useful — I hope there is time to add it in the next year.
There is, however, a fundamental problem with struct polymorphism:
As soon as you add members, you loose compatibility (a Point3D: Point2D can't be stored or passed where its parent is expected), and when you cross module borders, there might be issues as well.
Struct and class have to behave different in inheritance, and to disallow struct subtyping completely is just the easiest solution.
Imho it's definitely not the best solution, as it forces you to use classes whenever you want to use inheritance (I don't think that composition is superior in general).
It could be even possible to create structs that inherit from classes and vice versa.

Tino


(Joe Groff) #6

It sure seems natural.

Is there some reason the language can't allow a sub-struct to add member variables, such that the whole is treated like a contiguous set of members?

Class-style inheritance is tied to identity. Value types in Swift have no identity except for the value they contain, and they can be freely copied (or not) by the implementation. By adding fields to a struct, you change the value of that struct, and it doesn't make sense to say that a value with fewer fields "is-a" value with added fields. Those extra fields added by a sub-struct could be arbitrarily lost by copying (as often happens in C++ when copy constructors are invoked on inherited structs) and make defining equality difficult. Classes don't have these problems since each instance has a unique independent identity, and only references to that instance are passed around.

In my case, I have a rect-like value type, but I'd rather it inherit from CGRect, rather than contain a CGRect. That makes it much more natural to use.

Is allowing that just too complicated, or does it create type safety issues?

There is a different kind of inheritance that may make sense for value types. You can consider a type that can represent a proper subset (or the same set) of values from another type to be its subtype; for example, UInt8 could be a subtype of UInt16. If you have another rect-like type, which represents rectangles in a different way (perhaps using integer coordinates instead of Double, or using coordinate ranges instead of base + size), it might make sense for us to allow you to treat it as a subtype of CGRect for convenience.

-Joe

···

On Aug 1, 2016, at 5:28 PM, Rick Mann via swift-users <swift-users@swift.org> wrote:


(Brent Royal-Gordon) #7

Oops, forgot to finish a sentence:

···

On Aug 2, 2016, at 1:24 AM, Brent Royal-Gordon <brent@architechies.com> wrote:

This makes them small and fast, which are necessities if we're to use them to implement fundamental data types like `Int`. Basically, if structs weren't already so "primitive",

we would need to have *actual* primitive types to get the necessary performance.

--
Brent Royal-Gordon
Architechies


(Karl) #8

* Structs have statically-dispatched methods and properties; there's no ability to override.

I wonder if that is an inherent property of structs, or a side-effect from them having no inheritance. There is no way to define something else which also “is” a CGRect, so all structs are final, and final is what gives static dispatch.
If you access a struct via a protocol reference (PWT), you will get dynamic dispatch.

In my case, I have a rect-like value type, but I'd rather it inherit from CGRect, rather than contain a CGRect. That makes it much more natural to use.

I’m reading this to mean you want something which “is” a CGRect. If we allowed those kinds of relationships for structs, and when using the open base type:

- Methods would have to be dynamically dispatched unless declared final
- Instance size would not be known, requiring heap allocation and storing pointers when used as a member
- The sub-structs would lose information when bridged to their C equivalents. The CoreGraphics/UIKit framework code is not going to store and pass around your big CGRects

The problem you are facing is a library limitation. CoreGraphics and UIKit do not allow you to abstract the concept of a “rect”. You could suggest that they replace CGRect with an extendable abstraction (i.e. an open class or a protocol) to represent a rectangle. Those libraries follow this pattern in other areas - for example, UITextPosition and UITextRange are abstract classes and intended to be subclassed. However, replacing CGRect would break lots of existing code and possibly introduce performance regressions, and the benefits are not likely to be significant enough to warrant it.

Karl


(David Sweeris) #9

I thought the problem with struct polymorphism (specifically the stack size issue) *was* the fundamental reason we can’t have “substructs”.

- Dave Sweeris

···

On Aug 2, 2016, at 4:03 AM, Tino Heth via swift-users <swift-users@swift.org> wrote:

There is no fundamental reason to disallow struct inheritance, and I started a pitch where afair one member of core agreed it would be useful — I hope there is time to add it in the next year.
There is, however, a fundamental problem with struct polymorphism:
As soon as you add members, you loose compatibility (a Point3D: Point2D can't be stored or passed where its parent is expected), and when you cross module borders, there might be issues as well.
Struct and class have to behave different in inheritance, and to disallow struct subtyping completely is just the easiest solution.
Imho it's definitely not the best solution, as it forces you to use classes whenever you want to use inheritance (I don't think that composition is superior in general).
It could be even possible to create structs that inherit from classes and vice versa.


(Rick M) #10

What I used to do in C++ was subclass CGRect. In this way, I could pass my rect to CoreGraphics methods directly, because the any reference to my subclass started with the same bytes as the CGRect, and everything worked (not withstanding that most of CG copies rects, rather than operating on references to them).

Maybe this particular use case is too limited or specialized, but I wish I could do the same thing in Swift.

···

On Aug 2, 2016, at 07:48 , Karl <razielim@gmail.com> wrote:

* Structs have statically-dispatched methods and properties; there's no ability to override.

I wonder if that is an inherent property of structs, or a side-effect from them having no inheritance. There is no way to define something else which also “is” a CGRect, so all structs are final, and final is what gives static dispatch.
If you access a struct via a protocol reference (PWT), you will get dynamic dispatch.

In my case, I have a rect-like value type, but I'd rather it inherit from CGRect, rather than contain a CGRect. That makes it much more natural to use.

I’m reading this to mean you want something which “is” a CGRect. If we allowed those kinds of relationships for structs, and when using the open base type:

- Methods would have to be dynamically dispatched unless declared final
- Instance size would not be known, requiring heap allocation and storing pointers when used as a member
- The sub-structs would lose information when bridged to their C equivalents. The CoreGraphics/UIKit framework code is not going to store and pass around your big CGRects

The problem you are facing is a library limitation. CoreGraphics and UIKit do not allow you to abstract the concept of a “rect”. You could suggest that they replace CGRect with an extendable abstraction (i.e. an open class or a protocol) to represent a rectangle. Those libraries follow this pattern in other areas - for example, UITextPosition and UITextRange are abstract classes and intended to be subclassed. However, replacing CGRect would break lots of existing code and possibly introduce performance regressions, and the benefits are not likely to be significant enough to warrant it.

--
Rick Mann
rmann@latencyzero.com


(Tino) #11

I thought the problem with struct polymorphism (specifically the stack size issue) *was* the fundamental reason we can’t have “substructs”.

As I said:
I guess this is the reason we don't have "substructs" now — but inheritance can be useful without polymorphism:
Imagine an application that managers data of customers and employees.
It would be reasonable to model those data-containers as structs, and as both structs share many properties, a "person"-type would be quite convenient, even if it is not possible to create collections which contain customers as well as employees*.

Tino

* yes, an embedded "address"-struct would be an alternative — but copy & paste or writing everything in assembler is an alternative as well :wink:


(Brent Royal-Gordon) #12

The other alternative for this particular case is a Person protocol which Customer and Employee both conform to. Protocols currently require you to redeclare their properties, but we could add a feature to change that if there was a strong enough justification.

···

On Aug 2, 2016, at 8:44 PM, Tino Heth via swift-users <swift-users@swift.org> wrote:

* yes, an embedded "address"-struct would be an alternative — but copy & paste or writing everything in assembler is an alternative as well :wink:

--
Brent Royal-Gordon
Architechies


(Tino) #13

Protocols currently require you to redeclare their properties, but we could add a feature to change that if there was a strong enough justification.

Sometimes, this would be very handy (not only for structs) — but I'd expect that there are at least as many cases where you don't want that behavior…
It would be possible to silently generate readwrite-properties (and even functions returning void or an optional), but that feels a little bit to much magic for me.

Also, this would be a use-case for protocols that is completely different from their normal role.