英文:
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<ReaderContext<T>>
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> {
| +++
For more information about this error, try `rustc --explain E0107`.
error: could not compile `any_reader_test` (bin "any_reader_test") due to previous error
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
Can anyone help with this one ?
答案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: &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()
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论