2 ms·
Regarding Rust: > There is another thing I noticed, and I hope someone from the Rust community can correct me if I'm wrong, but it looks like Rust also special
by comex 10y ago
Regarding Rust:
> There is another thing I noticed, and I hope someone from the Rust community can correct me if I'm wrong, but it looks like Rust also special cased format_args! in the compiler by making it a built-in. If my understanding is correct, this would make Zig stand out as the only language of the three mentioned here which does not special case string formatting in the compiler and instead exposes enough power to accomplish this task in userland.
It depends what you mean by "userland". Rust has long supported procedural macros, albeit not on stable compiler versions until recently (and still only hackily there). Procedural macros, a.k.a. compiler plugins, are written in Rust, but work differently from compile-time function evaluation. With CTFE, the code running at compile time lives in the same "world" as the rest of the code being compiled, in a sort of pretend version of the target environment where you can't do things like call external functions. Rust compiler plugins are just separate libraries that get compiled for the host architecture and loaded into the compiler process (which is less scary than it sounds; the build process "just works" thanks to Cargo, and Rust's safety ensures you don't stomp on random compiler state). You have access to the full language and can call whatever you want, even do things like file I/O if needed; on the other hand, you're in a separate environment and can't just name variables, functions, etc. from the runtime world.
Currently, Rust compiler plugins are purely syntactic: you can look at the tokens of the macro invocation and that's it. You can't, say, ask the compiler for the definition of a type or the value of a named constant; plugins are executed too early for that. I think it'd be nice if that changed in the future, but it would be hard to implement without giving up the ability to emit arbitrary syntax as the output of the macro; there would be trouble with circular dependencies (think type inference).
But what currently exists is enough to implement safe printf, in conjunction with Rust's type system. format_args! is built into the compiler, so it could cheat, but it doesn't; like user-defined macros it's purely syntactic. The following Rust code:
println!("a={} b={}", a, b);
expands to something like this (I've simplified it a bit):
_print(Arguments::new_v1(&["a=", " b=", "\n"],
&[ArgumentV1::new(&a, Display::fmt),
ArgumentV1::new(&b, Display::fmt)]));
format_args! parses the format string and splits it up, and separates the arguments into a series of calls, without needing to know the types of a and b. ArgumentV1::new is just a regular generic function that can be called with any type implementing the Display trait. (Actually, it depends on the second argument, which is a function, but the details aren't important; there's nothing particularly special happening.)
Arguably it's nicer to be able to know the types of the arguments within the format parsing routine, and other use cases require that kind of feedback, but Rust's approach works pretty well here.
By the way, Rust also has a Turing-complete type system and a rudimentary form of compile-time function evaluation, which in the future might be powerful enough to express code similar to the Zig implementation, but for now it's not really designed for that sort of thing.
Sidenote: While C's metaprogramming is rather limited, C++14 CTFE is pretty powerful and you can parse format strings at compile time there too:
https://akrzemi1.wordpress.com/2011/05/11/parsing-strings-at-compile-time-part-i/ https://akrzemi1.wordpress.com/2011/05/11/parsing-strings-at...