www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - article comparing Rust and Zig, many points relevant to D

reply Mark <smarksc gmail.com> writes:
"Why I rewrote my Rust keyboard firmware in Zig: consistency, 
mastery, and fun"
https://kevinlynagh.com/rust-zig/

D is not mentioned, but the article focuses on issues of 
conditional compilation and compile-time execution, so I thought 
the D people will find it interesting.
Mar 09 2021
next sibling parent IGotD- <nise nise.com> writes:
On Tuesday, 9 March 2021 at 16:48:28 UTC, Mark wrote:
 "Why I rewrote my Rust keyboard firmware in Zig: consistency, 
 mastery, and fun"
 https://kevinlynagh.com/rust-zig/

 D is not mentioned, but the article focuses on issues of 
 conditional compilation and compile-time execution, so I 
 thought the D people will find it interesting.
Interesting article and it reaffirms what I originally observed with Rust and decided to look elsewhere. Rust for embedded systems is just too cumbersome due to the nature of embedded programming in my opinion and it gets quickly ugly when you step outside the normal application programming comfort zone. In this particular example, a keyboard controller, is a very simple case of embedded system and despite of that Rust just makes it difficult. Zig as handles that much easier and you can get from A to B without splitting any hairs. BetterC is a little bit what Zig offers and I would assume that BetterC would be able to handle the task in a similar fashion.
Mar 09 2021
prev sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Mar 09, 2021 at 04:48:28PM +0000, Mark via Digitalmars-d wrote:
 "Why I rewrote my Rust keyboard firmware in Zig: consistency, mastery,
 and fun"
 https://kevinlynagh.com/rust-zig/
 
 D is not mentioned, but the article focuses on issues of conditional
 compilation and compile-time execution, so I thought the D people will
 find it interesting.
Favorite quotes from the article:
 Despite all this, I still donโ€™t feel comfortable with Rust. It feels
 fractally complex โ€” seemingly every time I use Rust on a new project,
 I run into some issue that forces me to confront a new corner of the
 language/ecosystem.
 Rust has many language features and theyโ€™re all largely disjoint from
 each other, so knowing some doesnโ€™t help me guess the others.
These sound alarming to me, as someone who does not know Rust. Maybe I'm reading too much into it, but it does make me hesitant to try out Rust. While D is far from perfect, I think it does a lot better in the department of guessability. I've found IME D generally does what I thought it would do, except for the occasional hiccups, bugs, or unexpected corner cases. Also, IMO D does much better in avoiding fractal complexity -- the core language features generally integrate well with each other, and things generally work without too much nitpicking. Some of the newer features, however, feel like they're less integrated, and more edging towards fractal complexity territory.
 I was tempted to conclude that, well, all this complexity must be
 inherent. Perhaps itโ€™s just hard to do compile-time configuration and
 to iterate over distinct types efficiently in a safe, compiled
 language?
This was also how I felt in C/C++ before I found D. :-D I'd say that 70-80% of the time I spent writing C++ is spent wrestling with the complexity of the language and peripheral things like memory management concerns. It conditioned me to accept that programming is inherently complex, programming languages are consequently inherently complex, and therefore the act of programming must necessarily also be complex. Then I found D. Suddenly, many things that were complex in the past are now simpler, sometimes by a lot. I found my mental resources freed up to focus on the problem domain rather than wrestling with the language. D hits many of the points of friction with other languages IME: - The tedium of memory management in C/C++: thanks to the GC, my APIs are liberated from memory-management clutter, and my brain is freed from the constant drone of memory-related concerns to actually make progress in the problem domain. (And once in a while when the GC isn't good enough, D is flexible enough to allow me to rewrite select parts of the code with manually-managed memory -- without having to re-architect the entire program from scratch.) - Boilerplate in Java: when writing Java, I find myself fighting with the language more often than actually making progress with the problem domain. A lot of this has to do with metaprogramming features, that let me factor away the endless stacks of boilerplate that I'm forced to write in Java. - Too many (nested) loops: lately I've also been enjoying writing functional-style code with UFCS -- it's been a big time-saver. Instead of writing yet another loop, or worse, N-level-nested loops to express some complex data manipulation, a UFCS one-liner neatly encapsulates all of that complexity into a form not only fast to write, but also easy to read and easy to rearrange, extend, and refactor. - Single paradigm: languages like Java (or Haskell) force you to shoehorn every problem into an OO paradigm (resp. functional paradigm). Sometimes, some code is simply better to write in a different paradigm, but in these languages you're straitjacketed and have no choice, so you end up fighting the language more than making progress in the problem domain. D being multiparadigm is a big boon in this respect. D certainly isn't perfect -- it has its own share of dark corners, WATs, and poorly-interacting corner cases. But it's a lot better than the other alternatives I've tried so far. T -- My program has no bugs! Only unintentional features...
Mar 09 2021
parent reply Imperatorn <johan_forsberg_86 hotmail.com> writes:
On Tuesday, 9 March 2021 at 20:56:12 UTC, H. S. Teoh wrote:
 On Tue, Mar 09, 2021 at 04:48:28PM
๐Ÿ’ก Great summary! I don't understand why D isn't used more. Maybe it's *too* flexible for some people? ๐Ÿค”
Mar 09 2021
next sibling parent reply tsbockman <thomas.bockman gmail.com> writes:
On Tuesday, 9 March 2021 at 21:06:46 UTC, Imperatorn wrote:
 Great summary! I don't understand why D isn't used more.
D is my personal language of choice, but it is no mystery to me why it's been so slow to catch on. People always complain about the same (mostly valid) things when asked why they aren't using D (ascending order of importance): 4) Various out-dated criticisms like "the D compiler isn't open source": These complaints are invalid; catching people up on the history of the D project is all that is necessary to deal with them. 3) Small ecosystem: This is valid, but somewhat unfair given that D's lack of popularity is what keeps the ecosystem small. This concern is also mitigated, but not eliminated, by D's excellent interop with C (also Objective-C, Python, and maybe Java?). Getting C++ interop working more fully would help a lot, too, but that's a *very* hard problem. 2) Unsatisfying heap memory management options: D offers an easy to use but inefficient GC, and C-style unsafe manual memory management. However, what many of the people who would even *consider* switching to D really want is reference counting and borrowing (like C++, Rust, Swift, etc.), with as much compiler automation, optimization, and verification as possible. The D project has been pursuing this goal for years through improved RAII, nogc, scope, DIP 1000, live, etc. But, as of today, this subsystem just isn't good enough to replace the GC. No matter how much anyone on the forums claims that the current heap memory management story is good enough, it won't change the fact that many people who really do find D otherwise attractive are turned away by this limitation. Most of the people who only them. 1) Buggy/incomplete/absent tooling: This is the big one. I frequently encounter serious compiler bugs, the GDB debugger crashes or fails to properly inspect and step through my programs more often than not, and the D plugin for my IDE draws red squiggles and yellow highlighting all over my correct, valid, working code. I'm sure my experience in this regard is worse than most, since I make heavy use of various unpopular, incomplete, and new features. But, some of the problems I find and report are in really basic things, too. All of the above issues have improved a lot in the years that I have been using D, and I think these improvements are reflected in D's increasing adoption. I am very thankful for the hard work of all those people and institutions who have contributed to D's increasing quality. But, there is still a *long* way to go to catch up to the polished "it just works" experience of something like Java.
 Maybe it's *too* flexible for some people? ๐Ÿค”
There are some people who aren't interested in a complex, multi-paradigm language like D. There are many others who *are* interested, but they're stuck with C++ or have switched to Rust because of the issues with D that I listed above.
Mar 09 2021
parent SealabJaster <sealabjaster gmail.com> writes:
On Wednesday, 10 March 2021 at 00:14:18 UTC, tsbockman wrote:
 3) Small ecosystem
 ...
 However, what many of the people who would even *consider* 
 switching to D really want is reference counting and borrowing
And instead people see a bunch of different, incompatible memory management libraries for things like allocators, non-GC containers, and smart pointers, all of which are in varying states of completeness and buginess, and most of which aren't "standard" to use which makes it off-putting to spend any time at all with them. Not to mention that just in general, D libraries are mostly hit or miss in terms of quality and whether they even function properly at all, or are supported for more than a week after they're released. want, and you'll usually find a well-document, actively-developed/well-tested featureful alternative that has some expectation of doing what you want without too much pain. For example, 3 or so years ago me and a mentor wanted to write something that needed a database, and I thought it'd be a good time to introduce him to D. So after looking over and trying several database connectors on dub to varying levels of success (one couldn't handle SQL Server's nvarchar type; one couldn't connect for unknown reasons; one required external binaries we had trouble finding pre-compiled versions of and didn't want to spend time compiling it ourself, etc.). So in the end, we decided to go with Entity Framework, where everything basically just worked and it could even reverse-engineer our database structure into a EF model. Not to say EF doesn't have its warts, but we were up and going within a fraction of the time spent just finding something in D that could actually function at all. Meanwhile I had a sneaking suspicion that even if we got one of these libraries to work, there'd be some show-stopper bug that we'd run into even in our very simple use case. Not to mention the rich resources and documentation on the internet for EF, while for a dub library the best you usually get is the README, and if you're lucky, some example files, or depressingly, vice-versa. And I feel this is the point I often reach where I think: Is D really worth it for this project? Is D's productivity (as a language) going to be able to offset the un-productivity of its + all the other bells and whistles I need (docs, examples, tutorials, etc.)? I fear this is not uncommon, but I don't think there's any way to really measure this as a metric. However, ecosystem aside we still used D for some mild data processing and generation, which D is really enjoyable to make use of. That being said though, that was also a few years back, so I imagine it's somewhat better now, and there's a project I've been wanting to try to make so I might see for myself soon how things have improved.
 Buggy/incomplete/absent tooling
Slowly but surely. I'm quite happy with code-d now that it doesn't crash every time I make a new line, and that autocomplete doesn't suddenly stop working. But the squiggles, oh dear me the squiggles. Debugging is still annoying though, as you said. I know I'm always quite negative about D in general, but that's due to a desire of wanting it to grow and improve, rather than malice.
Mar 10 2021
prev sibling next sibling parent Max Haughton <maxhaton gmail.com> writes:
On Tuesday, 9 March 2021 at 21:06:46 UTC, Imperatorn wrote:
 On Tuesday, 9 March 2021 at 20:56:12 UTC, H. S. Teoh wrote:
 On Tue, Mar 09, 2021 at 04:48:28PM
๐Ÿ’ก Great summary! I don't understand why D isn't used more. Maybe it's *too* flexible for some people? ๐Ÿค”
I don't have any hard data to back this up, but people just haven't really heard of D. Even on hackernews most people aren't particularly familiar with the language, and they're the kind of people who go in for that kind of stuff
Mar 09 2021
prev sibling next sibling parent reply VF <nonexistent poomail.lol> writes:
On Tuesday, 9 March 2021 at 21:06:46 UTC, Imperatorn wrote:
 On Tuesday, 9 March 2021 at 20:56:12 UTC, H. S. Teoh wrote:
 On Tue, Mar 09, 2021 at 04:48:28PM
๐Ÿ’ก Great summary! I don't understand why D isn't used more. Maybe it's *too* flexible for some people? ๐Ÿค”
I think the biggest reasons are bugginess and instability. Those are my reasons for wanting to get out of D, and also feature bloat. I want a simpler language, but am not seeing good options... Speaking of bugginess, here's the latest bug that I noticed (dmd 2.094): mixin template X() { int x; } struct A { mixin X; int x; } // <- that compiles! int main() { import std.stdio; writeln(A.sizeof); // => 8 writeln(A.id.offsetof); // => 4 }
Mar 10 2021
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Mar 10, 2021 at 05:59:56PM +0000, VF via Digitalmars-d wrote:
[...]
 Speaking of bugginess, here's the latest bug that I noticed (dmd 2.094):
 
 mixin template X() { int x; }
 
 struct A { mixin X; int x; } // <- that compiles!
This is not a bug. It's a feature. (Although its value is debatable.) Mixins exist in separate namespaces, which can be useful if you have multiple mixins that declare the same identifier. Note that there's an optional identifier after `mixin X` that can be used to disambiguate between the two instances of `x`, e.g.: struct A { mixin X x2; int x; } A.x2.x = 1; a.x = 2; T -- In a world without fences, who needs Windows and Gates? -- Christian Surchi
Mar 10 2021
next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 10 March 2021 at 18:08:24 UTC, H. S. Teoh wrote:
 (Although its value is debatable.)
You use my jni.d... it uses this to store the java methodIds it looks up in the generated binding class. This feature makes that very, very easy to implement and use. I'm not sure how I'd do it without that... maybe a string mixin or something, like I'm sure I'd figure it out, but it is really nice when these things actually just work.
Mar 10 2021
parent reply mw <mingwu gmail.com> writes:
On Wednesday, 10 March 2021 at 18:13:09 UTC, Adam D. Ruppe wrote:
 On Wednesday, 10 March 2021 at 18:08:24 UTC, H. S. Teoh wrote:
 (Although its value is debatable.)
You use my jni.d... it uses this to store the java methodIds it looks up in the generated binding class. This feature makes that very, very easy to implement and use. I'm not sure how I'd do it without that... maybe a string mixin or something, like I'm sure I'd figure it out, but it is really nice when these things actually just work.
What is the exact usage of this feature? Can you give an example of your usage?
Mar 10 2021
parent Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 10 March 2021 at 21:51:43 UTC, mw wrote:
 What is the exact usage of this feature?
http://dpldocs.info/experimental-docs/source/arsd.jni.d.html#L1358 That _jmethodID is unique for each method added. So when you follow the example code: http://dpldocs.info/experimental-docs/arsd.jni.html and have like Export void hi(string name) { import std.stdio; writefln("hello from D, %s", name); } // D can also access Java methods Import void printMember(); Then hi and printMember both get their own private copy of the id. No conflict regardless of how many methods you mixin. Notice too how these mixin static constructors too, which are all combined per language rules, allowing me to actually concatenate the lists at startup for a one-go load of metadata at runtime. http://dpldocs.info/experimental-docs/source/arsd.jni.d.html#L1790 For the other side, adrdox uses that: https://github.com/adamdruppe/adrdox/blob/master/doc2.d#L3169 That's a mixin template with semi-specialized implementations of the abstract interface. But there's also cases like here: https://github.com/adamdruppe/adrdox/blob/master/doc2.d#L3028 That override the mixed in override, letting you accept the default implementations in some places, and selectively override them for other methods, without having to create another child class to do it. Now take a look at this line: https://github.com/adamdruppe/adrdox/blob/master/doc2.d#L3158 Notice the explicit usage of `this` there. Without it, it would use the local name inside the mixin template and NOT respect the override; this is good when you want to use a private member like in the jni example. But with it, it looks up one level above which means the user can easily override it and then it respects this, similar to a virtual vs static lookup in oop (which itself can get a bit nuts if you use the curiously-recurring template pattern, which I do here for reflection of a child class inside the base class http://dpldocs.info/experimental-docs/source/arsd.cgi.d.html#L8288 - the mixin template version is kinda the same idea.)
Mar 10 2021
prev sibling next sibling parent reply mw <mingwu gmail.com> writes:
On Wednesday, 10 March 2021 at 18:08:24 UTC, H. S. Teoh wrote:
 On Wed, Mar 10, 2021 at 05:59:56PM +0000, VF via Digitalmars-d 
 wrote: [...]
 Speaking of bugginess, here's the latest bug that I noticed 
 (dmd 2.094):
 
 mixin template X() { int x; }
 
 struct A { mixin X; int x; } // <- that compiles!
This is not a bug. It's a feature. (Although its value is debatable.) Mixins exist in separate namespaces, which can be useful if you have multiple mixins that declare the same identifier. Note that there's an optional identifier after `mixin X` that can be used to disambiguate between the two instances of `x`, e.g.: struct A { mixin X x2; int x; } A.x2.x = 1; a.x = 2;
If there is no optional identifier x2, a.x = 2; will change which x? and how to disambiguate the two x in such case?
Mar 10 2021
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Mar 10, 2021 at 09:49:12PM +0000, mw via Digitalmars-d wrote:
 On Wednesday, 10 March 2021 at 18:08:24 UTC, H. S. Teoh wrote:
[...]
 	struct A {
 		mixin X x2;
 		int x;
 	}
 
 	A.x2.x = 1;
 	a.x = 2;
 
If there is no optional identifier x2, a.x = 2; will change which x? and how to disambiguate the two x in such case?
[...] IMO this should generate an ambiguity error. But I don't know the current behaviour. T -- Perhaps the most widespread illusion is that if we were in power we would behave very differently from those who now hold it---when, in truth, in order to get power we would have to become very much like them. -- Unknown
Mar 10 2021
parent Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 10 March 2021 at 22:08:43 UTC, H. S. Teoh wrote:
 IMO this should generate an ambiguity error.  But I don't know 
 the current behaviour.
At the top level, it will always use the top-level name. This allows for easy overriding, like with the adrdox `declarationType` method in my previous message. mixin templates seem to be fairly poorly understood in the community. I wrote a little about them here: http://dpldocs.info/this-week-in-d/Blog.Posted_2020_01_20.html#understanding-mixin-templates about a year ago, but perhaps more needs to be written. They are NOT copy/pasted code, that's closer to what string mixin is (and it isn't copy/pasted code either, but rather copy/pasted AST nodes but I digress). The common name "mixin" makes you think they're the same, but they are very different beasts. Template mixins are more like a separate struct that allows name as well as virtual slot forwarding to the surrounding context (but notably operator overloading is NOT considered, and function overloading is not automatic, requiring an `alias` bridge since the mixin template and the parent are two separate contexts with name-based overriding, not signature-based). It is a fairly unique concept but the behaviors are all there for a reason and once you know those reasons, you can do a lot of cool things with them. The biggest worry is overloading constructors, there I'd argue it is a bug / oversight. You can alias in __ctor... sometimes. Otherwise the `this` keyword isn't allowed by the syntax meaning the proper procedure doesn't work. But most everything else works well once you get to know it.
Mar 10 2021
prev sibling next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 3/10/2021 10:08 AM, H. S. Teoh wrote:
 On Wed, Mar 10, 2021 at 05:59:56PM +0000, VF via Digitalmars-d wrote:
 [...]
 Speaking of bugginess, here's the latest bug that I noticed (dmd 2.094):

 mixin template X() { int x; }

 struct A { mixin X; int x; } // <- that compiles!
This is not a bug. It's a feature. (Although its value is debatable.) Mixins exist in separate namespaces, which can be useful if you have multiple mixins that declare the same identifier. Note that there's an optional identifier after `mixin X` that can be used to disambiguate between the two instances of `x`, e.g.: struct A { mixin X x2; int x; } A.x2.x = 1; a.x = 2;
You're right, it's quite deliberate. Template mixins would be fairly crippled if it didn't.
Mar 10 2021
prev sibling parent VF <nonexistent sewer.lol> writes:
On Wednesday, 10 March 2021 at 18:08:24 UTC, H. S. Teoh wrote:
 On Wed, Mar 10, 2021 at 05:59:56PM +0000, VF via Digitalmars-d 
 wrote: [...]
 Speaking of bugginess, here's the latest bug that I noticed 
 (dmd 2.094):
 
 mixin template X() { int x; }
 
 struct A { mixin X; int x; } // <- that compiles!
This is not a bug. It's a feature.
Oh dear God.
Mar 12 2021
prev sibling next sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 10 March 2021 at 17:59:56 UTC, VF wrote:
 Speaking of bugginess, here's the latest bug that I noticed 
 (dmd 2.094):

 mixin template X() { int x; }

 struct A { mixin X; int x; } // <- that compiles!
That's not a bug, it is working by design and it is a very useful design, allowing specialization of public members while keeping state of private members.
Mar 10 2021
prev sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Wednesday, 10 March 2021 at 17:59:56 UTC, VF wrote:
 [snip]

 I think the biggest reasons are bugginess and instability. 
 Those are my reasons for wanting to get out of D, and also 
 feature bloat. I want a simpler language, but am not seeing 
 good options...

 Speaking of bugginess, here's the latest bug that I noticed 
 (dmd 2.094):

 mixin template X() { int x; }

 struct A { mixin X; int x; } // <- that compiles!

 int main() {
     import std.stdio;
     writeln(A.sizeof);       // => 8
     writeln(A.id.offsetof);  // => 4
 }
I couldn't get this exact thing to run (the A.id.offsetof part and using int main instead of void main), but in general if you come across a bug you should file it in bugzilla. You need to be specific about what you think the bug actually is. According to the spec https://dlang.org/spec/template-mixin.html#mixin_scope there is no reason why this shouldn't compile as int x overrides the mixin. A reasonable question might be "if int x overrides what is in X, then why is it still a part of A"? There might be good reasons for why A.sizeof is 8 instead of 4.
Mar 10 2021
parent mw <mingwu gmail.com> writes:
On Wednesday, 10 March 2021 at 18:17:19 UTC, jmh530 wrote:
 On Wednesday, 10 March 2021 at 17:59:56 UTC, VF wrote:
 [snip]

 I think the biggest reasons are bugginess and instability. 
 Those are my reasons for wanting to get out of D, and also 
 feature bloat. I want a simpler language, but am not seeing 
 good options...

 Speaking of bugginess, here's the latest bug that I noticed 
 (dmd 2.094):

 mixin template X() { int x; }

 struct A { mixin X; int x; } // <- that compiles!

 int main() {
     import std.stdio;
     writeln(A.sizeof);       // => 8
     writeln(A.id.offsetof);  // => 4
 }
I couldn't get this exact thing to run (the A.id.offsetof part
I think he mean: A.x.offsetof
Mar 10 2021
prev sibling parent reply Guillaume Piolat <first.name spam.org> writes:
On Tuesday, 9 March 2021 at 21:06:46 UTC, Imperatorn wrote:
 On Tuesday, 9 March 2021 at 20:56:12 UTC, H. S. Teoh wrote:
 On Tue, Mar 09, 2021 at 04:48:28PM
๐Ÿ’ก Great summary! I don't understand why D isn't used more. Maybe it's *too* flexible for some people? ๐Ÿค”
There is PL adoption research: https://lmeyerov.github.io/projects/socioplt/papers/oopsla2013.pdf In this study they found the following drivers: 1. Open source libs 2. Extending existing code 3. Already used in group See that "Particular language feature" and "Simplicity" are not really ranked high. It seems over and over the one driver of PL adoption is always availability of "open-source libraries". In pure cynical terms it can be seen is a capital transfert between those maintaining the open source corpus vs those reaping added value with the open-source code ; in that it's code you can rely on, but don't pay for maintenance (or barely). But it can also signal a thriving/collaborating community (like with CPAN Perl). In many of the new "weaponized" languages created by Big Corp, a large corpus of the open-source ecosystem is created/maintained by the Corp ; value is instead seeked in the lockin that language provides.
Mar 11 2021
parent Imperatorn <johan_forsberg_86 hotmail.com> writes:
On Thursday, 11 March 2021 at 19:01:31 UTC, Guillaume Piolat 
wrote:
 On Tuesday, 9 March 2021 at 21:06:46 UTC, Imperatorn wrote:
 [...]
There is PL adoption research: https://lmeyerov.github.io/projects/socioplt/papers/oopsla2013.pdf [...]
Interesting! Will read ๐Ÿ“–
Mar 11 2021