How can i solve ownership issue in macro?

use proc_macro::TokenStream;
use std::any::Any;
use syn::{parse_macro_input, DeriveInput,Data,DataStruct,Fields};
use quote::quote;

#[proc_macro_derive(Builder)]
pub fn derive(input: TokenStream) -> TokenStream {
    // parse input
    let input = parse_macro_input!(input as DeriveInput);
    // get ident name
    let st_name = &input.ident;
    // get struct
    let fields = match input.data {
        Data::Struct(DataStruct { fields: Fields::Named(fields), .. }) => fields.named,
        _ => panic!("this derive macro only works on structs with named fields"),
    };
    // builder_fields
    let builder_fields = fields.iter().map(|field| {
        let field_name = &field.ident;
        let field_type = &field.ty;
        quote! {
            #field_name: Option<#field_type>
        }
    });
    // generate code
    let expand = quote!{
        pub struct CommandBuilder {
            #(#builder_fields),*
        }
        #[automatically_derived]
         impl #st_name {
            pub fn builder()->CommandBuilder {
                 CommandBuilder {
                    #(#builder_fields: None),*
                }
            }
        }

    };
    println!("{}","**************************************");
    println!("{:?}",expand.to_string());
    println!("{}","**************************************");
    TokenStream::from(expand)
}

i want to generate a struct and use it in the method, but i can't use #builder_fields twice in the code generation ; unfortunately using like this #(&#builder_fields),* does't work

#builder_fields Here is issue about builder_fields moved due to this method call, how can i solve this ?

Rust Playground

    let builder_fields = &fields.iter().map(|field| {
        let field_name = &field.ident;
        let field_type = &field.ty;
        quote! {
            #field_name: Option<#field_type>
        }
    }).collect::<Vec<_>>();

or Rust Playground

let builder_fields2 = builder_fields.clone();
...
#(#builder_fields2: None),*

Update: #(#builder_fields: None),* in your code seems incorrect expansion (didn't check it).

2 Likes

You are confusing the generated code with the generating code. In a quote!(), nothing but stuff inside interpolation syntax applies to the generating code. Taking the address result in literal code that takes the address of the expanded sub-expression.

You should just clone the iterator outside the quote, and declare it as a separate variable, then interpolate the two different variables inside the quotation.

1 Like

thanks

i tried all of them but still have issues looks like an interpretation problom

this is original calling

use derive_builder::Builder;

#[derive(Builder)]
pub struct Command {
    executable: String,
    args: Vec<String>,
    env: Vec<String>,
    current_dir: String,
}

fn main() {
    let mut builder = Command::builder();
    // builder.executable("cargo".to_owned());
    // builder.args(vec!["build".to_owned(), "--release".to_owned()]);
    // builder.env(vec![]);
    // builder.current_dir("..".to_owned());
}

all happend missing struct field . init in CommandBuilder seems fail

┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
error: comparison operators cannot be chained
  --> tests/01-parse.rs:26:10
   |
26 | #[derive(Builder)]
   |          ^^^^^^^ while parsing this struct
   |
   = help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
   = help: or use `(...)` if you meant to specify fn arguments
   = note: this error originates in the derive macro `Builder` (in Nightly builds, run with -Z macro-backtrace for more info)

error: comparison operators cannot be chained
  --> tests/01-parse.rs:26:10
   |
26 | #[derive(Builder)]
   |          ^^^^^^^ while parsing this struct
...
29 |     args: Vec<String>,
   |              ^
   |
   = help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
   = help: or use `(...)` if you meant to specify fn arguments
   = note: this error originates in the derive macro `Builder` (in Nightly builds, run with -Z macro-backtrace for more info)
help: split the comparison into two
   |
26 | #[derive(Builder && Vec)]
   |                  ++++++

error: comparison operators cannot be chained
  --> tests/01-parse.rs:26:10
   |
26 | #[derive(Builder)]
   |          ^^^^^^^ while parsing this struct
...
30 |     env: Vec<String>,
   |             ^
   |
   = help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
   = help: or use `(...)` if you meant to specify fn arguments
   = note: this error originates in the derive macro `Builder` (in Nightly builds, run with -Z macro-backtrace for more info)
help: split the comparison into two
   |
26 | #[derive(Builder && Vec)]
   |                  ++++++

error: proc-macro derive produced unparsable tokens
  --> tests/01-parse.rs:26:10
   |
26 | #[derive(Builder)]
   |          ^^^^^^^

error[E0063]: missing field `current_dir` in initializer of `CommandBuilder`
  --> tests/01-parse.rs:26:10
   |
26 | #[derive(Builder)]
   |          ^^^^^^^ missing `current_dir`
   |
   = note: this error originates in the derive macro `Builder` (in Nightly builds, run with -Z macro-backtrace for more info)
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈

test tests/02-create-builder.rs ... error
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
error: comparison operators cannot be chained
  --> tests/02-create-builder.rs:47:10
   |
47 | #[derive(Builder)]
   |          ^^^^^^^ while parsing this struct
   |
   = help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
   = help: or use `(...)` if you meant to specify fn arguments
   = note: this error originates in the derive macro `Builder` (in Nightly builds, run with -Z macro-backtrace for more info)

error: comparison operators cannot be chained
  --> tests/02-create-builder.rs:47:10
   |
47 | #[derive(Builder)]
   |          ^^^^^^^ while parsing this struct
...
50 |     args: Vec<String>,
   |              ^
   |
   = help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
   = help: or use `(...)` if you meant to specify fn arguments
   = note: this error originates in the derive macro `Builder` (in Nightly builds, run with -Z macro-backtrace for more info)
help: split the comparison into two
   |
47 | #[derive(Builder && Vec)]
   |                  ++++++

error: comparison operators cannot be chained
  --> tests/02-create-builder.rs:47:10
   |
47 | #[derive(Builder)]
   |          ^^^^^^^ while parsing this struct
...
51 |     env: Vec<String>,
   |             ^
   |
   = help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
   = help: or use `(...)` if you meant to specify fn arguments
   = note: this error originates in the derive macro `Builder` (in Nightly builds, run with -Z macro-backtrace for more info)
help: split the comparison into two
   |
47 | #[derive(Builder && Vec)]
   |                  ++++++

error: proc-macro derive produced unparsable tokens
  --> tests/02-create-builder.rs:47:10
   |
47 | #[derive(Builder)]
   |          ^^^^^^^

error[E0063]: missing field `current_dir` in initializer of `CommandBuilder`
  --> tests/02-create-builder.rs:47:10
   |
47 | #[derive(Builder)]
   |          ^^^^^^^ missing `current_dir`
   |
   = note: this error originates in the derive macro `Builder` (in Nightly builds, run with -Z macro-backtrace for more info)
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈

i remove this line #(#builder_fields),* from struct CommandBuilder and pub fn builder , then it runs and pass

Please show the macro expansion itself (cargo expand on nightly, or use the Playground).

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use proc_macro::TokenStream;
use std::any::Any;
use syn::{parse_macro_input, DeriveInput, Data, DataStruct, Fields};
use quote::quote;
#[proc_macro_derive(Builder)]
pub fn derive(input: TokenStream) -> TokenStream {
    let input = match ::syn::parse::<DeriveInput>(input) {
        ::syn::__private::Ok(data) => data,
        ::syn::__private::Err(err) => {
            return ::syn::__private::TokenStream::from(err.to_compile_error());
        }
    };
    let st_name = &input.ident;
    let fields = match input.data {
        Data::Struct(DataStruct { fields: Fields::Named(fields), .. }) => fields.named,
        _ => {
            ::core::panicking::panic_fmt(
                format_args!("this derive macro only works on structs with named fields"),
            );
        }
    };
    let builder_fields = &fields
        .iter()
        .map(|field| {
            let field_name = &field.ident;
            let field_type = &field.ty;
            {
                let mut _s = ::quote::__private::TokenStream::new();
                ::quote::ToTokens::to_tokens(&field_name, &mut _s);
                ::quote::__private::push_colon(&mut _s);
                ::quote::__private::push_ident(&mut _s, "Option");
                ::quote::__private::push_lt(&mut _s);
                ::quote::ToTokens::to_tokens(&field_type, &mut _s);
                ::quote::__private::push_gt(&mut _s);
                _s
            }
        })
        .collect::<Vec<_>>();
    let expand = {
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::__private::push_ident(&mut _s, "pub");
        ::quote::__private::push_ident(&mut _s, "struct");
        ::quote::__private::push_ident(&mut _s, "CommandBuilder");
        ::quote::__private::push_group(
            &mut _s,
            ::quote::__private::Delimiter::Brace,
            {
                let mut _s = ::quote::__private::TokenStream::new();
                {
                    use ::quote::__private::ext::*;
                    let mut _i = 0usize;
                    let has_iter = ::quote::__private::ThereIsNoIteratorInRepetition;
                    #[allow(unused_mut)]
                    let (mut builder_fields, i) = builder_fields.quote_into_iter();
                    let has_iter = has_iter | i;
                    let _: ::quote::__private::HasIterator = has_iter;
                    while true {
                        let builder_fields = match builder_fields.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                        if _i > 0 {
                            ::quote::__private::push_comma(&mut _s);
                        }
                        _i += 1;
                        ::quote::ToTokens::to_tokens(&builder_fields, &mut _s);
                    }
                }
                _s
            },
        );
        ::quote::__private::push_pound(&mut _s);
        ::quote::__private::push_group(
            &mut _s,
            ::quote::__private::Delimiter::Bracket,
            {
                let mut _s = ::quote::__private::TokenStream::new();
                ::quote::__private::push_ident(&mut _s, "automatically_derived");
                _s
            },
        );
        ::quote::__private::push_ident(&mut _s, "impl");
        ::quote::ToTokens::to_tokens(&st_name, &mut _s);
        ::quote::__private::push_group(
            &mut _s,
            ::quote::__private::Delimiter::Brace,
            {
                let mut _s = ::quote::__private::TokenStream::new();
                ::quote::__private::push_ident(&mut _s, "pub");
                ::quote::__private::push_ident(&mut _s, "fn");
                ::quote::__private::push_ident(&mut _s, "builder");
                ::quote::__private::push_group(
                    &mut _s,
                    ::quote::__private::Delimiter::Parenthesis,
                    ::quote::__private::TokenStream::new(),
                );
                ::quote::__private::push_rarrow(&mut _s);
                ::quote::__private::push_ident(&mut _s, "CommandBuilder");
                ::quote::__private::push_group(
                    &mut _s,
                    ::quote::__private::Delimiter::Brace,
                    {
                        let mut _s = ::quote::__private::TokenStream::new();
                        ::quote::__private::push_ident(&mut _s, "CommandBuilder");
                        ::quote::__private::push_group(
                            &mut _s,
                            ::quote::__private::Delimiter::Brace,
                            {
                                let mut _s = ::quote::__private::TokenStream::new();
                                {
                                    use ::quote::__private::ext::*;
                                    let mut _i = 0usize;
                                    let has_iter = ::quote::__private::ThereIsNoIteratorInRepetition;
                                    #[allow(unused_mut)]
                                    let (mut builder_fields, i) = builder_fields
                                        .quote_into_iter();
                                    let has_iter = has_iter | i;
                                    let _: ::quote::__private::HasIterator = has_iter;
                                    while true {
                                        let builder_fields = match builder_fields.next() {
                                            Some(_x) => ::quote::__private::RepInterp(_x),
                                            None => break,
                                        };
                                        if _i > 0 {
                                            ::quote::__private::push_comma(&mut _s);
                                        }
                                        _i += 1;
                                        ::quote::ToTokens::to_tokens(&builder_fields, &mut _s);
                                        ::quote::__private::push_colon(&mut _s);
        ::std::io::_print(
            format_args!("{0}\n", "**************************************"),
        );
    };
    TokenStream::from(expand)
}
const _: () = {
    extern crate proc_macro;
    #[rustc_proc_macro_decls]
    #[used]
    #[allow(deprecated)]
    static _DECLS: &[proc_macro::bridge::client::ProcMacro] = &[
        proc_macro::bridge::client::ProcMacro::custom_derive("Builder", &[], derive),
    ];
};

builder_fields: None causes the problem, the element in builder_fields contains the field name and type, but they are wrong syntax to initialize a value.

    let builder_fields = fields.iter().map(|field| {
        let field_name = &field.ident;
        let field_type = &field.ty;
        quote! {
            #field_name: Option<#field_type>
        }
    });
    let field_name = fields.iter().map(|field| &field.ident);

    // generate code
    let expand = quote! {
        pub struct CommandBuilder {
            #(#builder_fields),*
        }
         impl #st_name {
            pub fn builder()->CommandBuilder {
                 CommandBuilder {
                    #(#field_name: None),*
                }
            }
        }
    };

Rust Playground

2 Likes

Great :100: Thanks, solved this issue

No, I mean the generated code.

the issue is solved , and Thanks :pray: :pray:

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.