There one big issues, where I think D might make progress:
Combine GC and non-GC in a single application. This would be great for game programming for example. Handle audio and video in a soft-real-time non-GC thread. Handle other stuff like game logic in a GC thread, such that it is easier to write. The hard part is that GC usually requires stop-the-world. You can currently do this [0] in D, but it is error prone. Rust tackles this problem from the "other side": First make it safe, then possible.
The idea is to eventually have a Gc<T>-like type, like Rc<T>, which can be used on specific things. It will interact fine with the regular heap, so `Vec<Gc<Foo>>` where `Foo` contains a `Box<Gc<T>>` will still work.
You can then selectively apply this Gc<T> to things which need it. For example if you have a large graphy-datastructure in your code and don't want to bother with weak pointers. Or if you are doing FFI with a language that does have GC.
Rust itself would not have the Gc<T> type in the stdlib, it would only contain a couple of traits and compiler intrinsics that are useful for a GC. (The idea is to be able to turn on LLVM stack maps and use it for GC rooting). So you can write a gc for your specific use case (even if that use case is just safe bindings to a different GC from a runtime you are interacting with!) and it should work. In fact, in the design so far it seems like multiple GC implementations will be able to exist in the same application, and perhaps even safely interact (you can control whether you want this to be possible using the type system; I'm not sure if there is ever a case where this would be needed)
This would of course, still be safe. It would not be possible to violate GC invariants using safe code, nor would it be possible for the GC to violate regular safety invariants.
My biggest concern with the Gc ideas floating around Rust right now is that they may infect your program through dependencies.
I guess I'm mostly worried about Gc being used by a large number of libraries, to the extent that it might not matter if stdlib implements it or not, because by using some very popular library you'd end up pulling in this runtime dependency.
Part of me loves the flexibility this will bring to application design, but it could be a slippery slope.
If such a popular GC-using dependency came into existence, then there would surely be demand for a drop-in replacement that didn't use GC.
Remember, even the proposed Gc<T> type provides very little in the way of ergonomics (at least compared to garbage-collected types in pervasively-GC'd languages). Essentially it would just be a souped-up Rc<T> with no need for weak pointers to resolve cycles. Alternatively, it would only be used to interoperate with a foreign codebase that already uses GC, like SpiderMonkey or V8.
Given that people tend to only be writing Rust in the first place if they put a large degree of value on performant solutions, I'm not too worried about the library ecosystem suddenly sneaking in GC everywhere, especially since the Rc type is already halfway to garbage collection, and I don't see it used unduly in the wild.
It's not going to be something considered idiomatic, especially in a library. Rust support quite a few things which you're not supposed to use other than in exceptional circumstances (e.g. catching/recovering panics -- there has been no slippery slope yet).
Nor will the stdlib expose a GC API. I do intend on writing an example GC for this, but really each use case will probably need its own special GC implementation; since they are special use cases.
That's not a decision to take lightly.
I don't think GC will be something that can be silently pulled in, either, since you will need to change your codegen flags at the top level. This could be abstracted away by cargo, but it might be better if it wasn't; and you have to explicitly tell cargo in the toplevel toml file that you want GC to be on (and get a compile error if you try to compile without those flags).
==================
Also, the Rust community seems to be very careful about unnecessary costs. Even Rc<T> isn't used much, and when it is, it seems to be used at the application (not library) level.
When I have to box a closure or iterator in Rust because `-> impl Trait` doesn't exist (where you can return anonymous types by specifying the trait they implement), I feel so annoyed by the cost. As do many other Rustaceans. In reality, this is a pretty rare thing to do and the costs are negligible; but it feels annoying to not have the best performance. In such an environment, I doubt a library that frivolously uses GC will survive.
Another language you might consider is Nim which is more like D because it doesn't have specific selling point like Rust. It's very flexible and reasonable though and Python programmers will feel there immediately at home. You have GC there but you might opt out of it and/or let it run at specific times and for specific amount of time to manage gc pauses.
This article only covers the code. For the projects I work on (mostly C++ and/or C#), about 50% of decisions I make while coding is about data, not code.
IMO, Rust just not good enough for creating complex specialized data structures.
D surely has it’s problems as well (in short, I think the language itself is very good, but its runtime and libraries still need a lot of work), but at least it’s good for complex data structures.
I mean, you haven't actually pointed at a trivial tree implementation. The requirement that children be able to navigate to siblings is the issue here (complicating unique ownership by parents).
which your link even says. You would need a handle to the parent to surf around through siblings, is the limitation.
It's not really clear (to me) that the rctree is really all that complicated; the Rc stuff is there to clarify ownership: for how long should allocated memory stick around. It might just read weirdly due to familiarity, in the same way that D might weird me out.
I thought so too at first, but the thing about the limits imposed by the borrow rules is that they completely avoid the bugs that, in my experience, take the longest to track down in e.g. C++. It's the pain-up-front vs. more-pain-during-debugging argument. I've slowly been converted to the former camp from the latter as I write Rust full-time. The experience is just more... deterministic and pleasant. (Have you ever accidentally kept a pointer to an element of a std::vector too long and then caused the vector to reallocate?)
If you want you can always use raw pointers and dereference them in unsafe {} blocks, and even provide a safe API (return borrows with appropriate lifetimes) around this. You'll find plenty of common patterns in the standard library and on crates.io too.
In what way are data structures limited? Things like e.g. efficient intrusive data structures are difficult to implement in Rust without resorting to `unsafe`, but even if you do give in and start using unsafe code you've got no less safety than C++, since all C++ code is unsafe by Rust's standards anyway.
In C++, I can use pointers (or their equivalents like STL’s iterators or ATL’s positions) to combine simple built-in data structures into complex data structures to suit my needs.
For example, a vector that keeps some items + hash map that implements an index over that vector. Or some container that keeps some items + a linked list to track and evict the least recently used item from that container.
Rust is just too safe for that. That might be technically doable, but harder to implement, and harder to use.
I can believe that such a data structure would be harder to use, since if you bubble the unsafety upwards in your API then you'll force your users to use the `unsafe` keyword. But I'm afraid I don't see how this implies that data structures would be limited or harder to implement: raw pointers exist in Rust, and they're exactly as powerful and unrestricted as they are in C++. The only way it would be harder would be if you were trying to use references to ensure safety guarantees about your program above and beyond what C++ is capable of guaranteeing (i.e. doing more than what C++ does).
> See this example, the problem is trivial to solve in most
> languages
I disagree. That proposed code (again using references which provide guarantees that no other language provides) is failing because Rust statically prevents data races, which is not only non-trivial to solve in most languages, but actually impossible to solve in general in most languages. The way around this is to, again, regress to what other languages do and use raw pointers directly.
About raw pointers — no you can’t do same as in C++. There’s no malloc/free, and placement new is still unstable. Also they feel foreign to the language, and sometimes I can’t even get them from the standard collections (like when I need 2 mutable pointers to different elements).
About the safety, it’s important to understand there’s a price. In some cases, it causes simple things hard to implement, or cost performance.
For example, there’re algorithms out there that modify items of the same collection at the same time, both single threaded like sorting, and parallel.
Can such algorithm cause a data race? Yes.
Is it good the compiler tells me about that? Sure.
Is it good the compiler prevents me from doing that at all, and forces me to code some workarounds, that will cost me time to implement, will be ugly, and/or will sacrifice runtime performance? Not sure, I like having a choice.
That's a fair criticism, the onus is on us to get these features finalized and polished. :) Placement new is certainly going to be a high priority once MIR and specialization are wrapped up.
> Is it good the compiler prevents me
> from doing that at all
But it's not preventing you from doing anything, it's just telling you that it can't automatically prove that a use of references is safe. This is precisely what raw pointers are intended for: Rust isn't about entirely eliminating potential unsafety, it's about encapsulating it behind an "I know better than the compiler" interface that can be feasibly hand-audited.
> workarounds, that will cost me time to
> implement, will be ugly, and/or will
> sacrifice runtime performance
I still don't quite understand this criticism. If you were so inclined, you could forgo references altogether and code Rust as though it were C, with nothing but raw pointers, and it shouldn't take any more time to write than C, be no more ugly (syntactic differences aside), and have no difference in runtime performance.
> you could forgo references altogether and code Rust as though it were C, with nothing but raw pointers, and it shouldn't take any more time to write than C
Disagree.
In C++ we have sophisticated debuggers, we have standard C library, STL with its containers, algorithms and lots more, boost and other third-party libraries.
When coding typical Rust, we have descent-quality standard library, also big set of third-party libraries.
When coding Rust as though it were C, we have almost nothing, because the rust’s standard library doesn’t expose unsafe API.
Unsafe Rust only takes same time to write as C++ if you’re coding something trivial, like a “Programming 101” homework. For anything even remotely useful, debugger and libraries make huge difference.
A single bounds checking feature (C++ has that with STL containers in debug builds, controlled by _GLIBCXX_DEBUG or _ITERATOR_DEBUG_LEVEL) can sometimes save hours.
What features do you look for in a sophisticated debugger? There's upstream support for Rust in GDB (https://np.reddit.com/r/rust/comments/4merw3/gdb_now_support...), I've seen people do reverse debugging with rr (http://huonw.github.io/blog/2015/10/rreverse-debugging/) as well as Valgrind, and the compiler knows how to emit debuginfo, and so should work with any tool that can read DWARF (like LLDB). Mozilla is investing even more into dedicated IDE support this year and the next.
> the rust’s standard library doesn’t expose unsafe API
Well, here’s a simple example I’ve wrote above: “Some container that keeps some items + a linked list to track and evict the least recently used item from that container”.
You know, LRU caches are everywhere. I remember in C++, I recently wrote something similar in three completely unrelated projects. Well, a linked list alone isn’t usually enough, but a linked list + unordered map that maps elements to list iterators do the job very well. To update the LRU order, I query the hashmap and get a list position, then I move that element to the list’s tail. And when I need to evict an element, I grab the list’s head and evict those.
Note the following good things about this C++ solution:
(1) All those operations (hashmap lookup, linked list reorder, linked list remove head) are O(1), i.e. I can have millions items in my cache, and still update my LRU order for practically no cost (a few pointer writes).
(2) I didn’t wrote much code, only creatively combined what was already here in the C++ standard library.
Now Rust.
The safety means pointers to list nodes aren’t part of the list’s API.
And without raw pointers, linked lists are nearly useless, because we can’t split or reorder them efficiently. If you’ll read the documentation for LinkedList<T>::split_off, you’ll find the complexity is O(n), while for most other languages, linked lists are famous for being able to do that for O(1).
I’m not sure any efficient linked list API is possible within Rust memory management model, safe or unsafe.
> The safety means pointers to list nodes aren’t part of the list’s
> API.
A quick glance at LinkedList leads me to believe that there's no technical reason why we couldn't support a `split_at_node` function that takes a raw pointer to a list node (along with additional APIs that actually return raw pointers to list nodes, which themselves wouldn't need to be unsafe).
And even if my intuition is wrong and there's some reason why the implementation of LinkedList in the standard library can't do this for some reason, I see no additional reason why some other implementation of linked lists couldn't do this. (The standard library implementation likely optimizes for convenience rather than power, given that our data structures guru has a complicated relationship with linked lists: http://cglab.ca/~abeinges/blah/too-many-lists/book/README.ht... ). The fact that Rust doesn't favor these sorts of things hasn't stopped others from going ahead and writing implementations anyway: https://crates.io/search?q=intrusive
> I didn’t wrote much code, only creatively combined what
> was already here in the C++ standard library.
> I’m not sure any efficient linked list API is possible
> within Rust memory management model, safe or unsafe.
I'm still unsure what you're implying here, given that, again, unsafe code can use raw pointers willy-nilly (though as we've established, using the stdlib to compose data structures via raw pointers isn't always convenient).
> there's no technical reason why we couldn't support a `split_at_node` function that takes a raw pointer to a list node
How about rust memory management model?
When I split linked list in two, the new list must somehow become the owner of the newly detached elements.
For both unmanaged and garbage-collected languages there’s a heap (garbage collected or not) that’s shared across all threads of the process. List nodes are freed either explicitly with delete/free, or implicitly with GC.
Rust memory managed model forces to transfer the ownership when the list is split. AFAIK that’s exactly what happens inside split_off, and that operation is O(n).
> unsafe code can use raw pointers willy-nilly
If so, why the implementation of LinkedList<T> doesn’t? You know, O(1) and O(n) is a big difference.
> When I split linked list in two, the new list must
> somehow become the owner of the newly detached elements.
Yes, which is what `split_off` is already doing. It doesn't take unsafe code or breaking the "Rust memory management model" to achieve this; ownership transfer is a fundamental part of the language. The `split_at_node` function that I describe could simply copy the implementation of the existing `split_off` function while removing the part where `split_off` iterates through the list to find the node at the specified index (which is the O(n) part of the function).
> If so, why the implementation of LinkedList<T> doesn’t?
Because the stdlib doesn't want to expose too many unsafe APIs on its datastructures, giving it freedom to evolve the internals. And LinkedLists are considered a niche type anyway, not for general use.
As kibwen mentioned, you are free to create your own linkedlist implementation that exposes this API.
Rust has a heap, and you can free things directly if you need.
Let’s summarize. I want to implement efficient LRU cache data structure.
In C++, I don’t need to spend time implementing and debugging my own linked lists, because standard library implementation already good enough for the task. I only need to encapsulate two standard containers together, and write a pair of methods to add/remove stuff.
In Rust, I need to roll out my own lists, because stdlib doesn't want to expose too many unsafe APIs on its data structures.
You agree with all of the above?
If yes, hope now you see how Rust isn’t good enough for creating complex specialized data structures.
That's a non sequitur. The choice made by thee stdlib to not support many linked list operations (there probably are crates in the ecosystem for this anyway) does not imply Rust is unsuitable for complex specialized datastructures. It implies that Rust has opinions about what should and shouldn't be in the stdlib.
In fact, you said it yourself, "specialized". Since linkedlists are generally used in specific cases with different required ergonomics, you do not want to expose a catch-all.
Why not? An LRU cache is moderately complex specialized data structure. As you see, for some reasons, it’s much simpler to implement in C++ then in Rust.
> The choice made by thee stdlib to not support many linked list operations does not imply Rust is unsuitable for complex specialized data structures
I never said it’s unsuitable. Technically, all modern programming languages are Turing complete, so in some sense they’re equivalent. I only said Rust isn’t good enough for that. Meaning, in many other languages doing that is much simpler than in Rust.
Linked lists are special case of trees, which in turn special case of graphs. Both trees and graphs are used a lot: DOM trees, syntax trees, KD/PK trees, quad edge graphs, and so on.
Rust’s memory management model is neither manual nor garbage collected. This feature makes it hard to implement those pointer-based data structures, where items are in shared ownership of the container, where you want to split/merge those containers, and move items across containers.
Not impossible, just hard and/or inefficient (like those ref.counted implementation that increment/decrement those counters each time I traverse the graph, even if I only reading stuff).
> it’s much simpler to implement in C++ then in Rust.
Because of the standard library. This is not a language issue, this is a library issue. This has nothing to do with Rust's memory model. It has to do with Rust's choices of having a smaller, maintainable stdlib and relying on the crates ecosystem for most libraries (which can then evolve independently of the language, which is nice).
Someone has to write a linkedlist implementation of the type you describe and put it in a crate, and that's it; everyone can use that. It should be trivial to make a pull request to https://github.com/contain-rs/linked-list to add the method you describe, for example.
--------
Edit:
Rust does make it harder to implement the bottom level of low level abstractions (doubly linked lists, LRU caches, etc), since these deal with memory the most and can easily become unsafe. You implement them once with a safe API, and use them as much as you want.
The reason std doesn't expose too many unsafe APIs on its datastructures is because if you need that level of control, it is better to have your own implementation. Safety is hard; and the more unsafe APIs you expose, the more you run the risk of external unsafe code depending on the internals of the implementation in the stdlib. Due to this, it would be preferred if you write the LRU cache with a linkedlist implementation embedded in it (which could be a separate crate for reusability), with the invariants well documented. This is a choice made by the stdlib, but has nothing to do with the language itself and is a choice that external crates are free to not follow.
> Rust does make it harder to implement the bottom level of low level abstractions
Yeah, prohibitively harder. If you really need an efficient linked list, or tree, or graph, Rust makes it nearly impossible to accomplish.
> The reason std doesn't expose too many unsafe APIs on its datastructures is because if you need that level of control, it is better to have your own implementation
This might be true for Rust, but the argument is language-specific. In C++ I often need “that level of control” and yet I’m happy with many stock implementations. Moreover, I can combine different data structures to produce whatever I need. Can’t in Rust, because the library designers (not just std, the rest of them as well) think I better have my own implementation if I need that level of control.
> Safety is hard
Not necessarily. C# is very safe, and yet it’s much friendlier to developers: I never used unsafe C# code to overcome language limitations, only to interop with unmanaged code.
No it doesn't. It's literally adding one extra simple function to a library.
> Not necessarily.
Well-performing safety is hard, then. Safety is hard in C++ and Rust, less so in C#. Yes, it is easier to write a linked list in C++, but ensuring it is used safely is hard.
> Can’t in Rust, because the library designers (not just std, the rest of them as well) think I better have my own implementation if I need that level of control.
Right, and that is solely a library issue. Rust doesn't prohibit having composable unsafe libraries either. The ecosystem just hasn't produced those yet, because people probably didn't need it yet; and it is mostly preferred for libraries to keep the unsafety encapsulated inside behind a safe abstraction.
> No function will transfer ownership of N nodes to the new container for O(1) complexity.
No safe function. It is easy to do with unsafe. This has been explained already.
> And a tree. And a graph. Trees / graphs structures and algorithms are everywhere, too bad Rust language designers forgot that.
Rust's designers are mainly from the C++ world. They know this. And we have seen tons of these datastructures crop up that are written in Rust code everywhere.
The idea is that you find a safe abstraction boundary for you low level data structure, and stick to it. For an LRU cache, the boundary is the LRU cache (not the internal linked list, which needs to be accessed using `unsafe`). The stdlib exposes a linked list that can be used standalone, but as part of a larger unsafe datastructure you should copy it out and integrate directly to get finer grained unsafe access.
Sure, they wanted a safe language. What they actually did, they created a language where data is a second-class citizen.
The strategy “find a safe abstraction boundary for you low level data structure, and stick to it” works well for a scripting language. Or for high-level code that’s not performance-critical, like business logic.
It doesn’t work well for a general-purpose language that’s used for algorithms-heavy performance critical tasks.
There’s no “low level data structures”, because data structures are all the way down, and composed all the way down. Very often I need unsafe things at my API boundaries, because performance or because interop. I can’t implement my data structures once and stick to them, because as I work on the project I change both code and data structures (the main input here is profiler).
That safety thing makes data structures impractical. As you see from my linked list example, they are hard to create, use, and compose.
BTW, graphs (including trees and lists) are just one example how pointers are useful in data structures. Here’s another one.
In C++, I can add an index to a collection using unordered_map<tKey, Value*>. This index allows constant-time lookups, and if the tKey is something small like an int, the RAM cost is very low, because only keys + pointers are stored in the hash map, not the values themselves.
If I’ll want to do same in Rust, I suppose you’ll tell me I need to copy-paste the collection source code and add index there, because what’s in stdlib ain’t composable the way I want :-)
I agree about this. I wanted to give rust a go and tried to implement a graph for a DFA. I realized it was a pain. Even in general I find rust super hard to use with the references lifetimes etc. I don't understand the buzz going around rust when it's so hard to use in practice.
I am frustrated with new languages. I agree that C++ is old and needs a more modern replacement but:
- Go is weird with no templates and no functional programming.
- Nim comes close to my dream language but has a GC and some very awkward features like globally imported namespaces.
If I had to start a new project I would probably pick Nim or C++.
Yes, internals of data structures often need unsafe. At least with Cargo, and generics, you can implement them once, and then share them amongst projects. It's also why we have a fair number of data structures in the standard library; they will be audited a bit more heavily this way.
But only in a controlled way. Unsafe doesn't change the semantics of existing code, it only adds the ability to do a few extra things. So you're still going to get a lot of safety checks, even within an unsafe block. You still have to be careful, but it's not no-holds-barred.
Yeah. I've read your chapter on this repeatedly, and then the Rustinomicon, both hugely useful, but so far all I've done is use of for Rust -> C bindings and vice versa.
It's taken me a year to feel good about my Rust code, I'm still getting a handle on unsafe though.
The code linked above probably has bugs (for sure with singleton lists), but I hope you get the idea. It allows you to splice lists in and out in constant time.
Also, the code doesn't use `unsafe`. Now, you would need to use `unsafe` somewhere to actually create a list, in order to initialize the fields (or you could change the type of `next` to be `Option<...>`). However, the use of `unsafe` would be limited to that method. This tries to get at the difference between (i) occasionally using `unsafe` and (ii) having no safety guarantees at all.
There are probably lots of other pain points that you can bring up, and I'm not trying to say this should be good enough, but I thought it might help explain what is possible.
There is definitely a prospect! The core team understands the need. It still needs an RFC though, and once that is accepted it will have to make its way from nightly, to beta, then finally to stable. So alas, not any time soon. :(
I was responsible for this design decision. For me, shadowing is really useful and I use it all the time. It's especially useful for creating new, slightly different modifications of earlier values when you don't want to keep coming up with new names all the time—this is common when making heavy use of immutable variables.
Some people don't like it, and if you don't you can use clippy to disallow it. From what I've seen, most Rust code does use shadowing.
I used to write a lot of C++ and my idea of immutability always was that if I see a variable declaration at the top of a function and scroll down to where it's used (without reading the code in the middle) I can assume nothing happened to the variable.
Then I started to learn Haskell something like a year ago and found out that shadowing is not only allowed but used by people. Especially in Haskell (or OCaml for that matter) where you can just use x' for a slightly modified version of x. I don't know. Maybe I get used to it one day.
Since you don't use let for assignment, it's a non-problem. And it can be useful when you're doing some computation where you want to keep intermediate variables (for debugging or clarity), and rather than think of the proper name for each, you just successively bind the same name.
I know a lot of people really like shadowing. I obviously don't. GHC has an option to emit a warning and if Rusts compiler adds one for it as well (maybe it has one by know? I don't know) I guess I'm okay with it.
We have three, in fact, which disallow various kinds of shadowing. For example, some people like `let x = <expr containing x>` shadows but not `let x = <totally unrelated>`. But everyone has wildly different style choices here so we leave all three off by default and you can turn them on if you want. I have yet to see a bug in Rust code caused by shadowing, though.
Very useful indeed when you want a canonical or corrected (or otherwise "improved") version of a value and want to make sure that the original can't accidentally be used in what follows (which can be a hard to detect bug).
And is the behavior exactly the same as it would be if I omitted "let" on the second code line?
If the answer is yes, isn't it just mutability with different syntax then?
I would guess shadowing wouldn't affect values shared with a different thread, because it's technically a new value, but in single-thread use it would be equivalent to assignment.
The behavior is not the same. It's a new variable, entirely. So you'll have two integers on the stack, one of them is not accessible any more. You can see this difference if you add a scope around the second x, and then print the value of the original x after it goes out of scope.
Consider this program:
fn main() {
let x = 5;
let r1 = &x;
let x = x + 1;
let r2 = &x;
println!("r1: {:p}", r1);
println!("r2: {:p}", r2);
}
This will print something like
r1: 0x7fff955e3dfc
r2: 0x7fff955e3dec
Note that while you can't access the original x, it's still there, and references to it still work. Because the value of x was copied, not mutated.
Also, given compiler details like SSA, I'd argue that in a certain sense, mutation is similar to this, rather than this being similar to mutation...
It doesn't have the same semantics, because even after introducing the new variable, it is still immutable. So you cannot accidentally modify the new variable. e.g.,
let x = 1;
let x = x + 1;
x = 99; // Error: you can't accidentally modify the immutable variable `x`
let mut x = 1;
x = x + 1;
x = 99; // Ok
You can even turn back a mutable varaible into an immutable variable, like this:
let mut x = 1;
x = x + 1;
let x = x;
x = 99; // Error: `x` became immutable
This can be useful to "pin" the variable after a large number of calculations done to it.
I'm not an avid D user, but I have a guess as to why it is not so widely used.
To me, D is somewhat of a "I like C++s features and ergonomics and want a similar languages with all the features built in from the ground up" (+awesome metaprogramming). One of C++s drawbacks is that features have been bolted on in hacky ways due to backwards compatibility issues. D is like C++, but without this. It is a good goal, and from my limited experience with D I think they have achieved it well.
Thing is, such a language will largely be adopted by people who use C++. And the problem with these folks is that they already know C++. Once you are used to it it doesn't feel so bad. Despite its drawbacks, C++ has lots of tooling, a large ecosystem, and familiarity.
Rust is less affected by this for two reasons. One is that it gets a lot of production users from non-C++ shops who want to use a systems language to speed up their Python/etc tight loops. They would rather not worry about segfaults and stuff. The other is that for C++ users, Rust scratches a large itch. The learning curve for Rust is a lot worse than D, but for many the benefits might overcome this. You can't get used to segfaults the way you get used to template metaprogramming.
I would give D time. These issues just slow adoption, not stop it. That is fine.
Seems to go very much into details about syntax bike shedding but not saying much about the more "global" design differences.