I don't mean to complain. Doing software engineering for a living is a situation
of extreme privilege. But there's something to be said about how alienating it
can be at times.
Once, just once, I want to be able to answer someone's "what are you working
on?" question with "see that house? it wasn't there last year. I built that".
Instead for now, I have to answer with: "well you see... support for proc macros
was broken in rust-analyzer for folks who used a nightly rustc toolchain, due to
incompatibilities in the bridge (which is an unstable interface in the first
place), and it's bound to stay broken for the foreseeable future, not
specifically because of technical challenges, but mostly because of human and
organizational challenges, and I think I've found a way forward that will benefit
everyone."
Not really, no. At that point, the asker is usually nervously looking at the
time, and they start to blurt out some excuse to cut the conversation short,
just as I was about to ask them if they'd ever heard of Conway's
law.
Let's dig in.
There's always a lot of worry that Rust is defined by its "only implementation",
rustc. But that's not true!
Anyway it's still a work-in-progress and it definitely doesn't normate (?) the
version rustc I use daily. But it's a thing.
mrustc is also a thing! All I know
about it is that it's written in C++, for the only purpose of "bootstrapping"
Rust from C: you build (an older) rustc with it, and then you build a newer
rustc, and finally you have a working, recent rustc.
But those aren't overly relevant to today's topic.
So yeah! I didn't know that a month ago, but: rust-analyzer really does
re-implement a lot of things rustc does, in a slightly different way. And we
could spend time talking about rustc's librarification, but we're not going to.
Instead, we'll just let those decision inform our impression of rust-analyzer as
a separate project, with its own rules, its own design constraints, its own
team, its own release schedule, and its own wild success β at the time of this
writing, the VS Code
extension
alone has been downloaded almost a million times.
rust-analyzer even has its own "macros by example" engine.
...and that doesn't use any rustc machinery! It's all rust-analyzer doing its
business on its own.
However... there's another kind of macro: procedural macros, or proc macros for
short. And those are another story altogether.
Yes, that's the "procedural" in "proc macros".
Shell session
$ ./target/debug/deps/sample-603096a51bd3460d
Hi! sha512("hi") = [15, a, 14, ed, 5b, ea, 6c, c7, 31, cf, 86, c4, 15, 66, ac, 42, 7a, 8d, b4, 8e, f1, b9, fd, 62, 66, 64, b3, bf, bb, 99, 7, 1f, a4, c9, 22, f3, 3d, de, 38, 71, 9b, 8c, 83, 54, e2, b7, ab, 9d, 77, e0, e6, 7f, c1, 28, 43, 92, a, 71, 2e, 73, d5, 58, e1, 97]
Hi! sha512("hi") = [15, a, 14, ed, 5b, ea, 6c, c7, 31, cf, 86, c4, 15, 66, ac, 42, 7a, 8d, b4, 8e, f1, b9, fd, 62, 66, 64, b3, bf, bb, 99, 7, 1f, a4, c9, 22, f3, 3d, de, 38, 71, 9b, 8c, 83, 54, e2, b7, ab, 9d, 77, e0, e6, 7f, c1, 28, 43, 92, a, 71, 2e, 73, d5, 58, e1, 97]
Hi! sha512("hi") = [15, a, 14, ed, 5b, ea, 6c, c7, 31, cf, 86, c4, 15, 66, ac, 42, 7a, 8d, b4, 8e, f1, b9, fd, 62, 66, 64, b3, bf, bb, 99, 7, 1f, a4, c9, 22, f3, 3d, de, 38, 71, 9b, 8c, 83, 54, e2, b7, ab, 9d, 77, e0, e6, 7f, c1, 28, 43, 92, a, 71, 2e, 73, d5, 58, e1, 97]
And all of that makes sense... if you consider how proc macros must work.
The most important symbol exported by the proc-macro's shared object / dynamic
library is this one:
In other words: it's the proper representation to use if you're planning on
doing FFI (foreign function interfacing), which is very much what's happening
here.
And from there, if it needs to expand a macro, it just needs to find it by name
(by matching against the trait_name
/ name
), and then it uses the client
,
of type Client
:
Which... takes a server.