英文:
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()
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论