Why are structs and classes so different?

Ali Çehreli acehreli at yahoo.com
Tue May 17 16:00:04 UTC 2022


On 5/17/22 07:40, Kevin Bailey wrote:

 > Foo foo;
 >
 > is undefined behavior waiting to happen, that I can't detect at a glance.

Foo is null there. (I don't remember whether accessing through null 
reference is undefined or segmentation fault (on common systems).) Using 
foo will not do some random thing.

 > - A *secondary* goal would be for class objects to be able to have
 > deterministic destructors like structs.

I understand. D uses garbage collection by default for classes.

Having this choice of struct versus class is useful. This has been 
mentioned before: structs are the predominant user-defined type in D. 
Classes are only for polymorphic behavior.

 >> C++ does not use terms "value type" and "reference type" to make any
 >> distinction but the rules above are proof enough for me that C++
 >> implicitly divides user-defined types into such categories.
 >
 > I would beg to differ here. In C++, all types are value types, until you
 > add punctuation. This is one of the things that I like about C++, that I
 > trip over in other languages. e.g. In Rust, there are 2 similar types,
 > is it int and float, where one is copyable and the other move-only? How
 > can you write generic code in that environment?
 >
 > Yes, in C++, you have to worry about slicing and copying singletons, but
 > these are problems in front of you. It's the problems that sneak up
 > behind you that I worry about.

Yes, that's where we differ. :) You see problems that can sneak up, I 
see problems disappeared.

 >> Ok, I think I see better now. You would like members of a class
 >> recursively placed next to each other in memory. What if a polymorphic
 >> type had a polymorphic member?
 >
 > You mean like a string? I don't have a problem with this:
 >
 > class MyString {
 >     uint length;
 >     ...pointer to data...
 > }
 >
 > void func() {
 >     MyString s;

It is easy to adapt to the following syntax:

   auto s = /* some expression */;

   auto s = new MyString();
   auto s = scoped!MyString();
   scope s = new MyString();

MyString could have been a member variable, which would be initialized 
in a constructor (or not; everything gets the .init value).

 >     if (s.length == 0)  // I want this to be perfectly safe.
 >        writeln("empty");
 >     // 's' destroyed here, could do something useful

Such types are rare in my experience especially because of the GC. Once 
you remove explicit freeing of memory, most destructors disappear as 
well. In cases where deterministic destruction is needed, we have a 
number of options all of which were used by me:

- scope has already been mentioned.

- RAII: MyString could be owned by a struct (e.g. MyStringCloser) either 
for every use or wherever it makes sense.

- scope (exit) { s.close(); }

 > }
 >
 > In D, some objects can do this; some can't!

Perhaps in a template, yes. Then we may have to do some introspection there:

   static if (is (T == class)) {
     auto s = new T();
   } else {
     auto S = T();
   }

I've seen similar concerns raised about this difference before.

Just checked: Yes, there are 45 occurrences of that check under 
/usr/include/dlang/dmd on my system, which includes Phobos and core.

 > Python is a toy language, right? I'm not aware of any large projects in
 > it. (The largest I worked with was 138 files, 31k lines - tiny.)

Wow! That's way beyond my pay grade. :) But I have a feeling you will 
like D despite its differences.

 > Perhaps the project is small.

Correct. I would be happy if others chimed in but this struct/class 
difference is not a common issue with D at all.

 > Perhaps your IDE colors classes
 > brightly.

Yes, I use syntax highlighting with muted colors. :) I still use Emacs 
with a suboptimal configuration. (I need to work on fixing that already. :/)

Ali



More information about the Digitalmars-d-learn mailing list