Rust has three main forms of metaprogramming: generics, which are kind of like templates, but more like concepts (in C++ terms), macros, and compiler plugins.
Wait, Rust has macros that expand to code and mess up debugging, confuse tooling and everything else just like C++? Isn't that exactly what the language should have avoided?
C++ macros don't expand to actual semantic "code", in terms of an AST or even lexed terms. It blindly expand spans of textual characters, which I agree no post-C language should ever do again.
Languages as old as Lisp do AST-level macro expansion, with the actual programming language itself computing the expansion. Any code template blocks in the macro body are processed and validated in the native syntax of the language so that nesting and such aren't mungable.
This, per se, is not inherently worse, except that, oops: undefined behavior is worked into the spec. For instance, if you have a macro argument X which holds the ( token and another one Y which holds 123, and you paste these together using X ## Y, you get undefined behavior: two tokens are pasted to form something which is not a single, valid token.
A purely textual preprocessor wouldn't have an UB issue of this type.