Rust Vs Dlang,I want more experienced

I would add one more advantage of D over Rust: D has much better C++ interop. This is immensely important given the amount of C++ code already written. However, even D interop is far from seamless (https://dlang.org/spec/cpp_interface.html).

Although Rust interop with C is almost perfect, I doubt it would ever be possible to "match C++ virtual function table layout for single inheritance".

3 Likes

First things first:

  1. You did ask this in a Rust community, so I might be biased, but I will try to be as fair as I can.
  2. This of course are my personal opinions, people have different preferences so it is hard to say black-and-white that this is good and this is bad. Take it as "this might be true, I will take this into consideration and research these points and then build on that".
  3. I don't have much experience with D. I was interested in it at some point and researched it marginally. After finding that it does not bring much more than C++ to the table(Note that C++ has taken a lot of ideas from D and this is acknowledged. That's why it's not a big surprise that C++ contains most of D's features), I stopped investing time in it, so I am not very informed on what D is capable of.

Rust advantages over D:

  • At this moment, there is no other native language that can offer the same level of safety that Rust can offer. Even most of the garbage collected languages can't offer the same level of safety as Rust does(no data races).

  • Rust has a powerful macros system. You can for example, at compile time, download a file from the internet and then take its input to generate some data you will need at run-time - yes, that powerful.
    A good example of the power of the Rust macros system would be the regex crate, where it will check at compile-time if your regex is actually valid(and fail to compile if not) and the regex itself will turn into an optimized regex tailored to your specific need, it would be like a hand written optimized regex.
    Another example are database crates, like diesel that can make sure at compile-time that you don't make errors in your queries(not misspellings but actual errors). Or rust-postgres-macros that makes sure the SQL query is valid

  • Rust's type system(combined with the borrow checker) offers a lot of power to library writers.
    A library can force its users to use it correctly. Making sure users don't make mistakes.
    Here is an example how even in low-level code Rust type system can help enforce correct usage - note that the article is a bit more complex and understanding Rust semantics might be necessary.

There are probably more points but it's probably better to keep it shorter.
I think the first point should be enough to convince anybody.
I work at a company that uses C++ exclusively for its projects. The projects are very big and it's interesting that we are always left astonished with what kind of complex bugs cripple in, mostly related to threading but sometimes also related to memory management.
And I can't express how hard it is to find bugs in very big projects, it gets so complex.
We had one bug that we jumped around the code for a long time only to find in the end that that general function that was cleaning every time our vectors, was doing nasty things to a vector that was storing smart pointers, zeroing the memory, thus never getting the reference count to 0 and then free the heap allocated memory.
Now that I think about it, D actually can't help with any of our real issues while those bugs would of been avoided with Rust.


Rust disadvantages compared to D:
  • Because correctness is enforced(code logic can still be totally wrong), the learning curve in Rust is big, especially taking into consideration that D has C-like syntax, it is very familiar to most programmers(not beginners of course).
    And that's basically the main issue with Rust, it's learning curve. You have to decide if you want to invest time in understanding why the compiler doesn't allow you to compile that code in order to have all the advantages Rust offers you.

  • D has been in the wild for a longer time. My expectation would be that some libraries are in better state.
    I don't how D is doing at this category, but I think Rust doesn't yet have a big collection of good libraries.
    C++ for example is very rich in this area, take Boost or open github at any big company, like Facebook's Github for example. Only opening Facebook's Github you will find a big collection of high-quality C++ libraries.

In the end, for a language to take off(user base wise) it would need a big company backing it up.
Best example of this would be Apple with Objective-C(and now Swift). They didn't only promote it, they made it the de facto programming language to write software for their system.
To compare this to D and Rust, that would mean that Facebook would have to make D the de facto language to write Facebook plugins and Mozilla to make Rust the de facto language to write extensions for Firefox.
Of course these use cases cannot compete with writing full-fledged software for an operating system but it would change a lot.

5 Likes

Full disclosure, I've never wrote anything in D, but I'd like to expand on one point:

According to the dlang wiki, even though you can turn off the GC for your program, it prevents you from using a large portion of the standard library and some language features because the stand library was built with the assumption that a GC would be used. From what I understand, the language designer admits that they made a mistake with that particular decision regarding D and are working to fix the issue, but it's currently an extra hurdle to be considered if using a GC language isn't an option for the user.

1 Like

I was going to stay out of this, but I can't let this one stand. Rust's macros are a joke compared to simple CTFE + string mixins in D. Almost everything you've listed as an advantage of Rust over D is easier to do in D than it is in Rust. Not only that, but CTFE allows you to interact with the type system, something which is likely to be forever impossible with Rust macros.

The third advantage of Rust over D is also a bit questionable, but I don't want to get into subjective arguments.

5 Likes

You two are talking about different things. @LilianMoraru is talking about compiler plugins, and @DanielKeep is talking about macros by example.

If I was talking about macro_rules!, I'd have said those examples were impossible in Rust, not merely easier to do in D. Hell, I think bringing up compiler plugins is basically cheating (it's almost like saying "well, if you modify the compiler, you can do X..."), but like I said, I don't want to get into subjectives.

I do agree that D has really impressive metaprogramming, but "a joke" is a bit much. It's a strongly/weakly typed situation: they are more powerful in what they can accomplish, but our stuff can't generate invalid code, which D's can if mis-used.

We will eventually be as powerful and better in that sense, but for now, it's more of a trade off kind of situation.

3 Likes

Wasn't there a difference, where D templates/macros expand into arbitrary code, like C++ templates do? As opposed to Rust macros which expand into correct code.

Why? Aren't macros just a type of compiler plugins?

1 Like

I was bored one day, so I wrote something very much akin to Rust's println! in D. It was just some regular code that got executed at compile time. Completely stable, never broke, should have worked across all D compilers.

By contrast, you can't even write the formatting part of println! in macro_rules!; it has to be integrated into the compiler itself because no one sane wants to ship a compiler compiler to the package ecosystem that will break against random compiler versions.

Rust macros are great; they're not a joke in absolute terms. D is just so much better than Rust at this that comparing them is, frankly, silly. It's only a joke by comparison.

No, that's not true, either. String mixins are only allowed in specific places in the language grammar, just like macro invocations in Rust. Yes, you can have temporarily invalid constructs in existence during construction of the CTFE result, but the same is true of Rust using push-down macros.

Also, given that the vast majority of complex macros in my experience have to abuse the heck out of tt because Rust doesn't allow for deconstruction of substituted AST nodes (let alone parsing the language's grammar), I don't think the "strongly/weakly typed" metaphor is fair. The moment you go beyond basic matching and substitution, Rust macros turn into "untyped" token munchers.

I'm talking about CTFE and mixins, not templates. D templates have to be syntactically valid prior to expansion, and semantically valid after. You can't expand a D template to random gibberish. This is really no different to Rust macros, which behave in more or less the same fashion except that the macro's body doesn't have to be syntactically valid in the first place (beyond being a TT).

Technically yes, but it's not a useful distinction in this context.

Like I said, CTFE and string mixins are an actual part of D-the-language. Compiler plugins are rustc-specific. They're extra code that gets linked into the compiler. The only kind of macro that's stable and part of the language is macro_rules! which isn't even capable of anything described above.

In general, I never count unstable or implementation-specific features in these comparisons because it's wildly unfair and borderline deceptive, but I've had this flaming row before, and I'm not having it again. I haven't had nearly enough coffee, and it'd be getting off topic.


Look, I spent years abusing the hell out of D's metaprogramming abilities. I've also spent a lot of my time with Rust doing the same to macros. Rust macros have exactly one advantage over D CTFE+mixins that I can think of: they can leverage the Rust parser. I don't count being able to execute compiler plugins as a real advantage because those aren't stable and, as I said, could be done in the D compiler of your choice by modifying the compiler's source (which would also probably break about as frequently).

Playing up Rust's macros relative to D is both misleading (at best), and bad for Rust: it gives people who know Rust but not D the impression that they're not missing anything, that there's nothing D does better that Rust might learn and improve from.

13 Likes

Small comment about Andrei's 'jab': if you see Andrei talking at least once you'd know it was 100% a light-hearted jest. He does this all the time, even making jokes about himself.

You could take this 'jab' as a point against his comments on Rust, but please not as "this isn't as nice a community because he can say things like that". :slight_smile:

7 Likes

For context, the "skipped leg day" quote is from Andrei's answer to https://www.quora.com/Which-language-has-the-brightest-future-in-replacement-of-C-between-D-Go-and-Rust-And-Why, which is an interesting read - it did come across to me as a jest, though I've never interacted with Andrei or D before. I do know that jokes in written form can be tricky to get right!

Just reading that post comes across a little harsh to Rust, so you need to fill in the gaps a bit. Concretely, D and Go both have a GC listed in some form as a disadvantage. Rust doesn't have that listed (for obvious reasons) so you could consider it an advantage over the other two. "A historical lack of vision" is listed as a disadvantage of D and not of Rust, so "a history of good vision" would be an advantage of Rust over D. This need to fill in the gaps is because (as Andrei explicitly acknowledges) the measurements are according to some baseline, and (for example) no GC is treated as a baseline for systems languages.

1 Like

And to balance, here is one for the "Rust over D", which isn't mentioned frequently but, in my opinion, is among the most wonderful aspects of Rust.

In Rust, the same mechanism (namely traits) is used for both static and dynamic dispatch. Which means you don't have to decide virtual vs. non virtual when defining your types, because you can attach the vtable pointer to the value when you need it in code.

I've used existential types in Haskell before, but it happened only after I started coding in Rust that I understood how this feature is implemented and thought "wow, that's beautiful!".

7 Likes

I'm not playing sour puss here, In fact, I remember the quote so well because I suggested it as TWIR's quote of the week, adding that I deem it high praise. Who doesn't want a "bulging muscle"? It's the "little else" part of the quote that I personally find unconvincing after having written a few thousand lines of Rust code.

D is indeed a nice language, but it is still suffering from part of its ecosystem being incomplete. The GC in D does not work 24/7 as it leaks memory. Go has a big advantage here as it doesn't leak memory since Go 1.5 and is also quite performant (the GC in D isn't). The use of the GC in D is reflected in the base library. Major parts of it you can't use without having the GC turned on. The D people are currently working on having a separate base library for use with the GC and one without. D has something like unique_ptr in C++ which is called RefCounted. Unhappily, it only works with structs, but not with classes. All these issues might be fixed in this year or maybe another 1-2 years. However, D will always suffer from not having any backing from any notable company. So I don't think it will be chosen for real commercial projects and will always be some hobby language. There are some few exceptions, but really few like Sociomatic. But in total it amounts to almost nothing.

Agreed, it's quite a light-hearted comment (but still with a metaphor behind it). One can disagree with it, but I don't see how it can be taken as offensive or rude..

Hi,
Since you've spent some time with D, I wonder if you could give us an analogy of what would be the Rust equivalent of D mixins?

Would they be equivalent to, say, a syntax extension that compiles its inner code into a crate, then runs it feeding in AST of the original crate (which can be accessed via high-level API, similar to D's "traits"), captures string output, parses it, and finally substitutes the resulting AST at the expansion site?

Well, structural mixins are effectively taking an scope and copy+pasting its contents into some other scope. For example, you could define a template that contains a bunch of method definitions, then just mixin boilerplate!(); inside a bunch of classes/structs without having to actually copy+paste anything.

The closest analogue for Rust is probably just regular macros, though that's kinda dodgy given that macros don't interact with namespacing at all.

String mixins are a whole different cooking vessel of marine life. Rust would basically need a mixin keyword that takes a compile time constant string expression and "unquotes" it.

The problem with your proposed alternative is that it still doesn't allow for something as simple as:

// Note: probably not valid D; writing off the top of my head.
const string PREFIX = "foo_";

string gen_dbl(string name, string call) {
    return "int " ~ PREFIX ~ name ~ "() { return 2 * " ~ PREFIX ~ call ~ "(); }";
}

string gen_21(string name) {
    return "int " ~ PREFIX ~ name ~ "() { return 21; }";
}

mixin(gen_dbl("tim", "bob"));
mixin(gen_21("bob"));

void main() {
    assert(foo_tim() == 42);
}

You can't do that with just the AST, because the AST doesn't contain type or value information. Doing this at the syntactic level is just impossible.

Really, you need to mix this in with the semantic phase; you have to be able to do both constant expression evaluation and parsing.

Hmm, unless I am grossly misunderstanding your example, I don't see why any of that would require type information. Looks like a straightforward string concatenation...
Also, shouldn't gen_dbl() multiply by 2 or something?

A better example would have been to mixin a new function that you then call as part of generating another mixin function. However, even the above requires type information: how else do you know what PREFIX is? I mean, at the point macros are run, you don't even have names. PREFIX? What's a PREFIX? The compiler hasn't worked that out yet. It certainly doesn't know it's a string. It very certainly doesn't know what it's value is.

And even if you just fudge it so that simple cases like that work, all I have to do is put the value behind another layer of indirection and suddenly it stops working.

What're you talking about? It is multiplying by two.

*suspicious cough*

@DanielKeep You seem stressed, come down and relax a bit :sunglasses: