I want to implement a struct with a macro_rule. This is largely because the trait based generics require a lot of boilerplate and trait hunting.
The struct in question has a hash table inside but the key and the value types are to be provided by the user. The code is as follows:
macro_rules! new_ytz {
($T: ty) => {
// define the struct
pub struct Ytz {
table: hashbrown::hash_map::HashMap<$T, $T>,
}
impl Ytz {
pub fn new() -> Self {
Ytz {
table: hashbrown::hash_map::HashMap::<$T, $T>::new(),
}
}
pub fn add(&mut self, item: &$T) {
if self.table.contains_key(item) {
*self.table.get_mut(item).unwrap() += *item;
} else {
self.table.insert(*item, *item);
}
}
pub fn largest(&self) -> $T {
let mut result = 0;
for v in self.table.values() {
if result < *v {
result = *v;
}
}
result
}
}
// construct an instance of the struct and return it
Ytz::new()
};
}
// driver
fn main() {
let mut y = new_ytz!(u64); // should construct the object and return Ytz::new()
y.add(&71);
y.add(&25);
y.add(&25);
y.add(&25);
y.add(&34);
println!("{}", y.largest());
}
Obviously it won't compile since it tries to paste the struct within the main function. How can I work-around it? Meaning how can I paste the struct outside the main function publicly, along with the impl block?
Declarations of types in local blocks like that are only really intended to be used locally, as an implementation detail of generating the value that the block returns. If you want to declare global types, the best solution is to have a declare_ytz macro to declare it and then create instances of the corresponding type as necessary. Here's an updated version of the playground showing this approach. For full generality, you probably want to provide the struct name as a parameter to the macro as well, so you don't end up redeclaring the same struct.
If you want to define types within the body of a function and yet be able to return them, you will need to use at least a trait defined outside the function body and then use impl Trait as the (erased) type in the signature of the function:
pub
trait Ytz<T> {
fn new () -> Self
where
Self : Sized,
;
fn add (self: &'_ mut Self, item: &'_ T)
;
fn largest (self: &'_ Self) -> T
;
}
macro_rules! new_ytz {
($T: ty) => {{
// define the struct
struct Ytz {
table: std::collections::HashMap<$T, $T>,
}
impl $crate::Ytz<$T> for Ytz {
// ...
}
// construct an instance of the struct and return it
Ytz::new()
}};
}
// driver
fn mk_ytz () -> impl Ytz<u64>
{
let mut y = new_ytz!(u64); // should construct the object and return Ytz::new()
y.add(&71);
y.add(&25);
y.add(&25);
y.add(&25);
y.add(&34);
y
}
fn main ()
{
let y = mk_ytz();
println!("{}", y.largest());
}