I'll try to write a more thorough answer, but there is a wide range of topics to cover, so the following won't be necessary really well connected.
First of all, let's discuss parsing. Parsing certainly is not a bottleneck of rustc, and I would guess that even in DMD parsing per itself takes a small fraction of time (is this true?). rustc certainly leaves a bit of performance on the table during parsing. For example, it uses a hand written lexer instead of a giant generated state machine. But it shouldn't matter that much. I don't believe syntax can really affect the performance of the compiler (unless you make deliberately weird syntax, which can of course be even undecidable).
However, Rust syntax is pretty easy to write a LL style parser for (I did one). There are some warts, of course ( https://github.com/rust-lang/rfcs/pull/1685, or, from today's bug, {1} & 1
which is not a bitwise and), but in general syntax is really LLish and nice. Nothing close to the most vexing parse. Particularly, there is no problem with generics syntax, because in expression context generics always start with ::<
, and chaining comparison operators 1 < 2 > 3
is forbidden. There is no problem at all lexing <<
and <
, you just always lex them as separate tokens (or as a single token), and deal with consequences in the parser, which is easy (IIRC, there were some problems with <<
in macros, but they are fixed now). Stuff like optional semicolons after block-like expressions and restrictions that no struct literal can appear in the condition is more annoying, but bearable as well.
Next, let's talk about single pass compiler. I don't know what the official definition of the thing is, but in my mind single pass means that the compiler don't need to keep the code of all program in memory to compile it. This necessary requires forward declarations. So C compiler can be implemented in a single pass, because C has forward declarations, and despite the fact that lexing and semantic analysis of C are not independent (the infamous lexer hack). So, by my definition, dmd can't be a single pass compiler, because D does not require forward declarations. This makes performance of dmd even more impressive!
Now, to the rustc performance. First of all, rustc is horribly slow in my opinion (although it is absolutely great in all other aspects I am familiar with). There are two modes of slowness:
-
Slowness when compiling everything from scratch (make clean && make build
). This is pretty slow, because the run time is dominated by the code generation in LLVM. My rough measurements (2kloc projects in C++ and Rust) show that in this mode clang++ and rustc perform roughly the same. I also wonder how dmd compares with ldc in compile times. IIRC dmd uses a custom backend, specifically tailored for compilation speed (like Go, which is also famous for compilation speed).
-
Slowness when compiling after editing a couple of files. This boils down to compilation modes. Rust and C++ and perhaps D are all based on the notion of compilation units, which can be compiled independently. Generally, only modified compilation unit and its dependencies need to be recompiled. However, in C++ compilation unit is a single file, while in Rust compilation unit is a whole library (a crate). So, when you develop a library in Rust, you need to recompile it fully after every change, and this is double plus unfun. Although a better incremental compilation on the function level is being worked on. This is the change I have been waiting for since I've written my first thousand lines of Rust code 
So C++, Rust and D are pretty similar in the compilation model. But there is a big difference in how templates are compiled, which potentially can give an edge in compile times to Rust. In C++, templates are type checked at instantiation time, which means that every time you instantiate a template with a different type parameter, you have to do some amount of work again. In Rust, templates are checked at definition time only once. However this is only a marginal advantage, because you need to generate different code for different instantiations of templates anyway, and codegen is the actual bottleneck of the compiler.
My understanding is that compiling a library with templates in Rust will basically store parsed templates in the .rlib
, and all the code generation will happen when you instantiate those templates in the binary which links to the library. There is an idea that you can do some pre codegen optimizations once per template, such that the amount of code to generate will be smaller. This is the potential benefit of Rust compilation model / type system which could be leveraged to improve compilation speed.