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

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

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:

    1. pub trait AnyReaderContext
    2. {
    3. fn my_derived_function();
    4. }

    cargo.toml:

    1. [package]
    2. name = "any_reader_context"
    3. version = "0.1.0"
    4. edition = "2021"
    5. [dependencies]
  • Crate any_reader_context_derive

    lib.rs:

    1. use proc_macro::TokenStream;
    2. use quote::quote;
    3. use syn;
    4. #[proc_macro_derive(AnyReaderContext)]
    5. pub fn any_reader_context_derive(input: TokenStream) -> TokenStream {
    6. let ast = syn::parse(input).unwrap();
    7. // Build the trait implementation
    8. impl_any_reader_context(&ast)
    9. }
    10. fn impl_any_reader_context(ast: &syn::DeriveInput) -> TokenStream {
    11. let name = &ast.ident;
    12. let gen = quote! {
    13. impl AnyReaderContext for #name {
    14. fn my_derived_function() {
    15. println!("Hello, Macro! My name is {}!", stringify!(#name));
    16. }
    17. }
    18. };
    19. gen.into()
    20. }

    cargo.toml:

    1. [package]
    2. name = "any_reader_context_derive"
    3. version = "0.1.0"
    4. edition = "2021"
    5. [lib]
    6. proc-macro = true
    7. [dependencies]
    8. syn = "2.0.26"
    9. quote = "1.0"
  • Main crate

    main.rs

    1. use any_reader_context_derive::AnyReaderContext;
    2. use any_reader_context::AnyReaderContext;
    3. #[derive(AnyReaderContext)]
    4. pub struct Reader<T> {
    5. value: T
    6. }
    7. impl<T> Reader<T> {
    8. fn new(value: T) -> Self {
    9. Self {value}
    10. }
    11. }
    12. fn main() {
    13. let readers: Vec<Box<dyn AnyReaderContext>> = vec![];
    14. readers.push(Reader::<u32>::new(42));
    15. }

    cargo.toml

    1. [package]
    2. name = "any_reader_test"
    3. version = "0.1.0"
    4. edition = "2021"
    5. [dependencies]
    6. any_reader_context = { path = "../any_reader_context" }
    7. 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:

  1. $ cargo build
  2. Compiling any_reader_context v0.1.0 (rust-sandbox/any_reader_context)
  3. Compiling any_reader_test v0.1.0 (rust-sandbox/any_reader_test)
  4. error[E0107]: missing generics for struct `Reader`
  5. -->
  6. src/main.rs:5:12
  7. |
  8. 5 | pub struct Reader<T> {
  9. | ^^^^^^ expected 1 generic argument
  10. |
  11. note: struct defined here, with 1 generic parameter: `T`
  12. -->
  13. src/main.rs:5:12
  14. |
  15. 5 | pub struct Reader<T> {
  16. | ^^^^^^ -
  17. help: add missing generic argument
  18. |
  19. 5 | pub struct Reader<T><T> {
  20. | +++

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

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

    1. pub trait AnyReaderContext
    2. {
    3. fn my_derived_function();
    4. }

    cargo.toml:

    1. [package]
    2. name = &quot;any_reader_context&quot;
    3. version = &quot;0.1.0&quot;
    4. edition = &quot;2021&quot;
    5. [dependencies]
  • Crate any_reader_context_derive

    lib.rs:

    1. use proc_macro::TokenStream;
    2. use quote::quote;
    3. use syn;
    4. #[proc_macro_derive(AnyReaderContext)]
    5. pub fn any_reader_context_derive(input: TokenStream) -&gt; TokenStream {
    6. let ast = syn::parse(input).unwrap();
    7. // Build the trait implementation
    8. impl_any_reader_context(&amp;ast)
    9. }
    10. fn impl_any_reader_context(ast: &amp;syn::DeriveInput) -&gt; TokenStream {
    11. let name = &amp;ast.ident;
    12. let gen = quote! {
    13. impl AnyReaderContext for #name {
    14. fn my_derived_function() {
    15. println!(&quot;Hello, Macro! My name is {}!&quot;, stringify!(#name));
    16. }
    17. }
    18. };
    19. gen.into()
    20. }

    cargo.toml:

    1. [package]
    2. name = &quot;any_reader_context_derive&quot;
    3. version = &quot;0.1.0&quot;
    4. edition = &quot;2021&quot;
    5. [lib]
    6. proc-macro = true
    7. [dependencies]
    8. syn = &quot;2.0.26&quot;
    9. quote = &quot;1.0&quot;
  • Main crate

    main.rs

    1. use any_reader_context_derive::AnyReaderContext;
    2. use any_reader_context::AnyReaderContext;
    3. #[derive(AnyReaderContext)]
    4. pub struct Reader&lt;T&gt; {
    5. value: T
    6. }
    7. impl&lt;T&gt; Reader&lt;T&gt; {
    8. fn new(value: T) -&gt; Self {
    9. Self {value}
    10. }
    11. }
    12. fn main() {
    13. let readers: Vec&lt;Box&lt;dyn AnyReaderContext&gt;&gt; = vec![];
    14. readers.push(Reader::&lt;u32&gt;::new(42));
    15. }

    cargo.toml

    1. [package]
    2. name = &quot;any_reader_test&quot;
    3. version = &quot;0.1.0&quot;
    4. edition = &quot;2021&quot;
    5. [dependencies]
    6. any_reader_context = { path = &quot;../any_reader_context&quot; }
    7. 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:

  1. $ cargo build
  2. Compiling any_reader_context v0.1.0 (rust-sandbox/any_reader_context)
  3. Compiling any_reader_test v0.1.0 (rust-sandbox/any_reader_test)
  4. error[E0107]: missing generics for struct `Reader`
  5. --&gt; src/main.rs:5:12
  6. |
  7. 5 | pub struct Reader&lt;T&gt; {
  8. | ^^^^^^ expected 1 generic argument
  9. |
  10. note: struct defined here, with 1 generic parameter: `T`
  11. --&gt; src/main.rs:5:12
  12. |
  13. 5 | pub struct Reader&lt;T&gt; {
  14. | ^^^^^^ -
  15. help: add missing generic argument
  16. |
  17. 5 | pub struct Reader&lt;T&gt;&lt;T&gt; {
  18. | +++
  19. For more information about this error, try `rustc --explain E0107`.
  20. 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:

  1. $ cargo build
  2. Compiling any_reader_test v0.1.0 (/home/tcravic/workspace/avionics/prototypes/rust-sandbox/any_reader_test)
  3. error: expected `where`, `{`, `(`, or `;` after struct name, found `&lt;`
  4. --&gt; src/main.rs:5:21
  5. |
  6. 5 | pub struct Reader&lt;T&gt;&lt;T&gt; {
  7. | ^ expected `where`, `{`, `(`, or `;` after struct name
  8. 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

  1. fn impl_any_reader_context(ast: &syn::DeriveInput) -> TokenStream {
  2. let name = &ast.ident;
  3. let (impl_generics, type_generics, where_clause) = ast.generics.split_for_impl();
  4. let gen = quote! {
  5. impl #impl_generics AnyReaderContext for #name #type_generics #where_clause {
  6. fn my_derived_function() {
  7. println!("Hello, Macro! My name is {}!", stringify!(#name));
  8. }
  9. }
  10. };
  11. gen.into()
  12. }
英文:

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

  1. fn impl_any_reader_context(ast: &amp;syn::DeriveInput) -&gt; TokenStream {
  2. let name = &amp;ast.ident;
  3. let (impl_generics, type_generics, where_clause) = ast.generics.split_for_impl();
  4. let gen = quote! {
  5. impl #impl_generics AnyReaderContext for #name #type_generics #where_clause {
  6. fn my_derived_function() {
  7. println!(&quot;Hello, Macro! My name is {}!&quot;, stringify!(#name));
  8. }
  9. }
  10. };
  11. gen.into()
  12. }

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:

确定