Help: surround the struct literal with parentheses

The project structure and code looks like below:

.
├── Cargo.toml
├── reflect-derive
│   ├── Cargo.toml
│   └── src
│       └── lib.rs
└── src
    ├── lib.rs
    └── main.rs
// reflect-derive/src/lib.rs
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 name = input.ident;

    let output = quote! {
        impl Reflective for #name {
            fn name(&self) -> String {
                String::from(#name)
            }
        }
    };

    TokenStream::from(output)
}
// src/lib.rs
pub use reflect_derive::Reflective;

pub trait Reflective {
    fn name(&self) -> String;
}
// src/main.rs

use reflect::Reflective;

#[derive(Reflective)]
struct Foo {}

fn main() {
    println!("Hello, world!");
}
# 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 think I'm missing something. Thanks in advance.

This line looks fishy. Could you try adding quotation marks around #name? Like String::from("#name").

1 Like

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.

You're on the right track. Idk how I've missed it. This time, the problem is this:

// src/main.rs
use reflect::Reflective;

#[derive(Reflective)]
struct Foo {}

fn main() {
    let foo = Foo {};
    println!("{}", foo.name());
}

This will print out #name instead of Foo. I'm looking for a way to use those markers (?) inside string literals. If you know a way, let me know.

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)
    ...
}
2 Likes

and

...solved the issue. Thanks.

The final code is:

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.

Again, it expands to a string literal. A string literal does not have type String. It's &'static str.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.