如何使用Derived-macro属性将一个通用结构体存储在Vec中?

huangapple go评论87阅读模式
英文:

How can I use Derived-macro attribute to store a generic struct in a Vec?

问题

I'm working on implementing dynamic listeners for the communication layer of my embedded program.

I want to store a Vec<Box<dyn AnyReaderContext>> in my Readers structure. Since T can change, I need to hide it behind a trait: Vec<Box<dyn AnyReaderContext>> (following example of this SO question.)

I tried to follow the how-to-write-a-custom-derive-macro guide of the rust book, and I have the following minimal code:

  • Crate any_reader_context

    lib.rs:

    pub trait AnyReaderContext
    {
        fn my_derived_function();
    }
    

    cargo.toml:

    [package]
    name = "any_reader_context"
    version = "0.1.0"
    edition = "2021"
    
    [dependencies]
    
  • Crate any_reader_context_derive

    lib.rs:

    use proc_macro::TokenStream;
    use quote::quote;
    use syn;
    
    #[proc_macro_derive(AnyReaderContext)]
    pub fn any_reader_context_derive(input: TokenStream) -> TokenStream {
        let ast = syn::parse(input).unwrap();
    
        // Build the trait implementation
        impl_any_reader_context(&ast)
    }
    
    fn impl_any_reader_context(ast: &syn::DeriveInput) -> TokenStream {
        let name = &ast.ident;
        let gen = quote! {
            impl AnyReaderContext for #name {
                fn my_derived_function() {
                    println!("Hello, Macro! My name is {}!", stringify!(#name));
                }
            }
        };
        gen.into()
    }
    

    cargo.toml:

    [package]
    name = "any_reader_context_derive"
    version = "0.1.0"
    edition = "2021"
    
    [lib]
    proc-macro = true
    
    [dependencies]
    syn = "2.0.26"
    quote = "1.0"
    
  • Main crate

    main.rs

    use any_reader_context_derive::AnyReaderContext;
    use any_reader_context::AnyReaderContext;
    
    #[derive(AnyReaderContext)]
    pub struct Reader<T> {
        value: T
    }
    
    impl<T> Reader<T> {
        fn new(value: T) -> Self {
            Self {value}
        }
    }
    
    fn main() {
        let readers: Vec<Box<dyn AnyReaderContext>> = vec![];
        readers.push(Reader::<u32>::new(42));
    }
    

    cargo.toml

    [package]
    name = "any_reader_test"
    version = "0.1.0"
    edition = "2021"
    
    [dependencies]
    any_reader_context = { path = "../any_reader_context" }
    any_reader_context_derive = { path = "../any_reader_context_derive" }
    

any_reader_context and any_reader_context_derive crates compile properly.

Main crate has the following cryptic compilation error:

$ cargo build
   Compiling any_reader_context v0.1.0 (rust-sandbox/any_reader_context)
   Compiling any_reader_test v0.1.0 (rust-sandbox/any_reader_test)
error[E0107]: missing generics for struct `Reader`
 -->
src/main.rs:5:12
  |
5 | pub struct Reader<T> {
  |            ^^^^^^ expected 1 generic argument
  |
note: struct defined here, with 1 generic parameter: `T`
 -->
src/main.rs:5:12
  |
5 | pub struct Reader<T> {
  |            ^^^^^^ -
help: add missing generic argument
  |
5 | pub struct Reader<T><T> {
  |                  +++

I tried to add a second <T>, but obviously, the compiler didn't like it:

$ cargo build
   Compiling any_reader_test v0.1.0 (/home/tcravic/workspace/avionics/prototypes/rust-sandbox/any_reader_test)
error: expected `where`, `{`, `(`, or `;` after struct name, found `<`
 -->
src/main.rs:5:21
  |
5 | pub struct Reader<T><T> {
  |                     ^ expected `where`, `{`, `(`, or `;` after struct name

error: could not compile `any_reader_test` (bin "any_reader_test") due to previous error
英文:

I'm working on implementing dynamic listeners for the communication layer of my embedded program.

I want to store a Vec&lt;ReaderContext&lt;T&gt;&gt; in my Readers structure. Since T can change, I need to hide it behind a trait: Vec&lt;Box&lt;dyn AnyReaderContext&gt;&gt; (following example of this SO question.)

I tried to follow the how-to-write-a-custom-derive-macro guide of the rust book, and I have the following minimal code:

  • Crate any_reader_context

    lib.rs:

    pub trait AnyReaderContext
    {
        fn my_derived_function();
    }
    

    cargo.toml:

    [package]
    name = &quot;any_reader_context&quot;
    version = &quot;0.1.0&quot;
    edition = &quot;2021&quot;
    
    [dependencies]
    
    
  • Crate any_reader_context_derive

    lib.rs:

    use proc_macro::TokenStream;
    use quote::quote;
    use syn;
    
    #[proc_macro_derive(AnyReaderContext)]
    pub fn any_reader_context_derive(input: TokenStream) -&gt; TokenStream {
        let ast = syn::parse(input).unwrap();
    
        // Build the trait implementation
        impl_any_reader_context(&amp;ast)
    }
    
    fn impl_any_reader_context(ast: &amp;syn::DeriveInput) -&gt; TokenStream {
        let name = &amp;ast.ident;
        let gen = quote! {
            impl AnyReaderContext for #name {
                fn my_derived_function() {
                    println!(&quot;Hello, Macro! My name is {}!&quot;, stringify!(#name));
                }
            }
        };
        gen.into()
    }
    

    cargo.toml:

    [package]
    name = &quot;any_reader_context_derive&quot;
    version = &quot;0.1.0&quot;
    edition = &quot;2021&quot;
    
    [lib]
    proc-macro = true
    
    [dependencies]
    syn = &quot;2.0.26&quot;
    quote = &quot;1.0&quot;
    
  • Main crate

    main.rs

    use any_reader_context_derive::AnyReaderContext;
    use any_reader_context::AnyReaderContext;
    
    #[derive(AnyReaderContext)]
    pub struct Reader&lt;T&gt; {
        value: T
    }
    
    impl&lt;T&gt; Reader&lt;T&gt; {
        fn new(value: T) -&gt; Self {
            Self {value}
        }
    }
    
    fn main() {
        let readers: Vec&lt;Box&lt;dyn AnyReaderContext&gt;&gt; = vec![];
        readers.push(Reader::&lt;u32&gt;::new(42));
    }
    

    cargo.toml

    [package]
    name = &quot;any_reader_test&quot;
    version = &quot;0.1.0&quot;
    edition = &quot;2021&quot;
    
    [dependencies]
    any_reader_context = { path = &quot;../any_reader_context&quot; }
    any_reader_context_derive = { path = &quot;../any_reader_context_derive&quot; }
    

any_reader_context and any_reader_context_derive crates compile properly.

Main crate has the following cryptic compilation error:

$ cargo build
   Compiling any_reader_context v0.1.0 (rust-sandbox/any_reader_context)
   Compiling any_reader_test v0.1.0 (rust-sandbox/any_reader_test)
error[E0107]: missing generics for struct `Reader`
 --&gt; src/main.rs:5:12
  |
5 | pub struct Reader&lt;T&gt; {
  |            ^^^^^^ expected 1 generic argument
  |
note: struct defined here, with 1 generic parameter: `T`
 --&gt; src/main.rs:5:12
  |
5 | pub struct Reader&lt;T&gt; {
  |            ^^^^^^ -
help: add missing generic argument
  |
5 | pub struct Reader&lt;T&gt;&lt;T&gt; {
  |                  +++

For more information about this error, try `rustc --explain E0107`.
error: could not compile `any_reader_test` (bin &quot;any_reader_test&quot;) due to previous error

I tried to add a second &lt;T&gt;, but obviously, the compiler didn't like it:

$ cargo build
   Compiling any_reader_test v0.1.0 (/home/tcravic/workspace/avionics/prototypes/rust-sandbox/any_reader_test)
error: expected `where`, `{`, `(`, or `;` after struct name, found `&lt;`
 --&gt; src/main.rs:5:21
  |
5 | pub struct Reader&lt;T&gt;&lt;T&gt; {
  |                     ^ expected `where`, `{`, `(`, or `;` after struct name

error: could not compile `any_reader_test` (bin &quot;any_reader_test&quot;) due to previous error

Can anyone help with this one ? 如何使用Derived-macro属性将一个通用结构体存储在Vec中?

答案1

得分: 2

fn impl_any_reader_context(ast: &syn::DeriveInput) -> TokenStream {
    let name = &ast.ident;
    let (impl_generics, type_generics, where_clause) = ast.generics.split_for_impl();
    let gen = quote! {
        impl #impl_generics AnyReaderContext for #name #type_generics #where_clause {
            fn my_derived_function() {
                println!("Hello, Macro! My name is {}!", stringify!(#name));
            }
        }
    };
    gen.into()
}
英文:

You completely ignore the generics which you simply can't do. You could simply forward them from the ast :

fn impl_any_reader_context(ast: &amp;syn::DeriveInput) -&gt; TokenStream {
    let name = &amp;ast.ident;
    let (impl_generics, type_generics, where_clause) = ast.generics.split_for_impl();
    let gen = quote! {
        impl #impl_generics AnyReaderContext for #name #type_generics #where_clause {
            fn my_derived_function() {
                println!(&quot;Hello, Macro! My name is {}!&quot;, stringify!(#name));
            }
        }
    };
    gen.into()
}

huangapple
  • 本文由 发表于 2023年7月17日 23:07:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/76705814.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定