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 Reader
structure. Since T
can change, I need to hide it behind a trait: Vec<Box<dyn AnyReaderContext>>
(following 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
# Cargo.toml
[package]
name = "any_reader_context"
version = "0.1.0"
edition = "2021"
// lib.rs
pub trait AnyReaderContext {
fn my_derived_function();
}
Crate any_reader_context_derive
# 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"
// 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()
}
Main crate
# 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" }
// 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));
}
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
Can anyone help with this one?
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()
}