Will the Rust developers do something about macros? To fix them somehow, it is possible to add comptimes.
There is a post on Reddit, but it is not clear what will happen to macros.
What do you want changed about macros?
I want macros to be clear and easy to learn. Or comptimes
What do you mean by "comptimes"? Faster compile times?
Comptime that is in Zig
The question has a positive note, without negativity. I would like to discuss what prospects there are in this direction.
From looking into comptime
in Zig, it seems the corresponding feature in Rust would be const
: const
blocks for forcing compile-time evaluation (not needed in the initializer of const
and static
items, which are always evaluated at compile time), and const
generics for compile-time parameters. The one difference from Zig's comptime
seems to be that Zig doesn't require functions to be marked as usable with it, whereas Rust requires functions to be marked as const
to use them in const
contexts (this allows changing the function body without changing where it can be used, and separates the checking of a function's const
compatibility from where it is used).
When people talk about comptime
they really talk about Zig's implementation of reflection and compile-time code generation.
Technically, in Zig, comptime
function is close to const fn
in Rust (or, even closer, to immediate function in C++).
But the trick that Zig employes, literally, everywhere, is the fact that these functions, because they are executed during compilation, can accept and return types and function bodies (in Rust, I guess, they would have to also accept and return traits).
It's really cool way of doing metaprogramming, much more flexible and argonomic that Rust's macros.
And, of course, Zig implements generics, async
and other things using that approach, too.
The usual retort on Rust side is that comptime
makes it impossible to do the type of analysis that compiler can do to traits… but that only makes sense when comptime
is considered as replacement to generics.
When we are talking about macros… comptime
is much easier to use on may levels – although macros have advantages, too: you can do cargo expand
and get decent approximation of what is actually processed… with comptime
that's not really possible.
A tangent: I think that there’s a more general missing tool / unsolved problem here. Not to do with macros vs. comptime
, but the general principle that (in certain types of situations) it’s harder to inspect some “thing” when the thing is “the behavior of some code” rather than “some data”.
For another example, suppose your program A is manipulating a library data structure B, which then misbehaves or ends up in the wrong state. You want to file a bug report with B, but it should be minimal and clear, not bringing in the specifics of “what program A is doing”. It would be useful if there was some automatic way to, given a specified boundary between A and B, record everything that is done to B, and produce a new program of straight-line code that does the same sequence of operations to B.
This requires a lot of reflection capability to implement, but if it existed, it would be, in a way, the equivalent of macro-expanding non-macros: automatically replacing an algorithm with its output.
(Of course, there are cases where such an automatically generated “script” would itself be illegible, or fragile due to run-time nondeterminism.)
Another method of doing this kind of meta-programming in rust is a build script.
You can run arbitrary rust code before compiling your src code. The output of which can be more src code that gets compiled (I think!). It lacks the ergonomics of comptime
, a. because you are dealing with strings that happen to become code structures once compiled, and b. because it's tucked away in a separate file and would really struggle to insert stuff in situ amongst the rest of your source code.
In principle, I suppose there's nothing stopping you from parsing your source code using syn and manipulating it to create more source code. But you're left to your own devices once you step outside of rust's macro system.
I admire Zig's comptime capabilities, I think they provide an awesome amount of flexibility. Rust has other strengths
I've recently posted in a similar thread on Zulip, about how I think Rust should strive to make it's non-proc macros more like Crystal. It would be a huge improvement in ergonomics, while being way less drastic of a change than types-as-values and Zig-style comptime.
I just went through a bender to write some rust macros. The error messaging of macros is just horrible. I ended up with doing a lot of
cargo expand
and adding horrible things like
let output_str = quote!{
// your quote here
}.to_string();
return quote! {
compile_error!(#output_str);
}
But even then writing my derive
macro was much more difficult than I'm used to with the rest of rust's error messages.
This is practically what derive
and attribute-style proc-macros do. The issue is that it operates at the level of AST tokens. There is zero information available from the type system at this level.
A lot can be done without type info, regardless. And it's far better than working with the declarative macro_rules
DSL. But improvements would be welcomed.
Introwospection was a great attempt to shift the status quo, but, unfortunately, it didn't go well due to untechnicall reasons.
The main advantage of TMP and comptime
is the fact that you can ignore combinations of things that are useless for you.
The main disadvantage of TMP and comptime
is the fact that you can ignore combinations of things that are useless for you.
That's very hard to handle with macros or build scripts.
If you'll think about it… generics and templates are like thiserror vs anyhow: handle all cases precisely vs let the developer sort out the mess.
Yet with error handling people accept the fact that there are two valid approaches, but with generic programming everyone have an opinion and assert that only one or the other is “valid”.
rust does have planned features for constant blocks that evaluate to const generics... perhaps we could extend that to having const blocks that evaluate to type generics?
this would basically give use the power of zig comptime, and it would do it without making it a disparate thing from rust's existing generics.