# Cargo.toml
[package]
name = "reflect"
version = "0.1.0"
edition = "2021"
[dependencies]
reflect-derive = { path = "reflect-derive" }
# reflect-derive/Cargo.toml
[package]
name = "reflect-derive"
version = "0.1.0"
edition = "2021"
[dependencies]
quote = "1.0.26"
syn = "2.0.15"
[lib]
proc-macro = true
This is not a serious project, I just want to understand procedural macros, that's all. What I want to do is to write a derive macro. The problem is, compiler complains saying:
error[E0423]: expected value, found struct `Foo`
--> src/main.rs:4:8
|
4 | struct Foo {}
| ^^^ not a value
|
help: surround the struct literal with parentheses
|
4 | struct (Foo {})
| + +
For more information about this error, try `rustc --explain E0423`.
error: could not compile `reflect` due to previous error
I don't exactly know why, but I always find these error messages... hilarious. Not at all in a negative way, sometimes it just leads to a really good laugh. I remember experiencing #106720 first-hand, and it really made my day.
Still (and somewhat unfortunately), they are bugs and thus must be fixed.
An identifier will print as an identifier, and a string literal will be interpolated as a string literal. "#name" is just a string literal. There's no chance quote! will look into it – if it did, then you couldn't spell a literal "#name" (and it would be surprising in a bad way, anyway).
Just convert the name to a string upfront, and interpolate that:
let name_str = name.to_string();
quote! {
...
String::from(#name_str)
...
}
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
#[proc_macro_derive(Reflective)]
pub fn reflect_derive_fn(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let ident = input.ident;
let ident_name = ident.to_string();
let output = quote! {
impl Reflective for #ident {
fn name(&self) -> String {
String::from(#ident_name)
}
}
};
TokenStream::from(output)
}
Inside quote!, using #ident_name does not work for some reason despite its type being String, I needed to do either String::from(#ident_name) or #ident_name.to_string(). I kinda understand that I guess? #ident is type Ident and inside quote!, it resolves into Foo identifier. I don't know what #ident_name expands into, though. Whatever, it works.