Rust Vs Dlang,I want more experienced


#6

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.


#7

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


#8

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.


#9

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.


#10

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?


#11

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.


Why not just add classes?
Improvement to syntax extensions
#12

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:


#13

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.


#14

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!”.


#15

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.


#16

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.


#17

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…


#18

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?


#19

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.


#20

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?


#21

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*


#22

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


#23

Whoops! Sorry. Was looking at it on a small phone screen yesterday, and somehow missed the “2 *” :hushed:

[quote=“DanielKeep, post:21, topic:4472”]
A better example would have been to mixin a new function that you then call as part of generating another mixin function. [/quote]
Rust syntax extensions can produce AST that contains other macros. Which could happen to be another instances on my hypothetical compiling syntax extension.

PREFIX could have been a macro that expands to a string.

I believe that your examples so far could be made to work without access to results of the type inference pass, or even the name resolution pass.

Now, if one could reflect on a type of a local variable, whose type is inferred… but can D do that?


#24

And one more for D: it supports design by contract https://dlang.org/spec/contracts.html


#25

It’s unfinished, buggy, and if you don’t have some help from the compiler that catches some precondition/invariant violations at compile-time, they are not much better than just asserts… (and I use DbC in D a lot).