Как реализовать макрос, который определяет новый общедоступный тип и возвращает экземпляр этого типа?
Я хочу реализовать структуру, используя macro_rules!
потому что для дженериков требуется много шаблонов и поиска признаков.
Рассматриваемая структура имеет внутри хеш-таблицу, но ключ и типы значений должны быть предоставлены пользователем. Код выглядит следующим образом:
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());
}
Это не будет компилироваться, так как пытается вставить структуру в основную функцию:
error: expected expression, found keyword `pub`
--> src/main.rs:4:9
|
4 | pub struct Ytz {
| ^^^ expected expression
...
40 | let mut y = new_ytz!(u64); // should construct the object and return Ytz::new()
| ------------- in this macro invocation
Как я могу это обойти? Как я могу публично вставить структуру вне основной функции вместе сimpl
блок?
1 ответ
дженерики требуют много шаблонов
use std::collections::HashMap;
use core::hash::Hash;
use std::ops::AddAssign;
struct YtzU64<T: Eq + Ord + Hash + Copy + AddAssign> {
table: HashMap<T, T>
}
impl<T: Eq + Ord + Hash + Copy + AddAssign> YtzU64<T> {
pub fn new() -> Self {
Self {
table: HashMap::new()
}
}
pub fn add(&mut self, item: &T) {
if let Some(item) = self.table.get_mut(item) {
*item += *item;
} else {
self.table.insert(*item, *item);
}
}
pub fn largest(&self) -> Option<T> {
let mut values = self.table.values();
let mut largest:Option<T> = values.next().map(|t| *t);
for v in values {
if largest < Some(*v) {
largest = Some(*v);
}
}
largest
}
}
fn main() {
let mut y = YtzU64::new();
y.add(&71);
y.add(&25);
y.add(&25);
y.add(&25);
y.add(&34);
println!("{}", y.largest().unwrap());
}
Мой перевод вашего макроса требует меньше шаблонов, чем ваш макрос. В нем на два отступа меньше, на 4 строки меньше (macro_rules!, сопоставление с образцом вверху, две закрывающие фигурные скобки в конце). Обратите внимание, что я немного изменил api, так какlargest
теперь возвращает Option
, чтобы соответствовать std::iter::Iterator::max()
. Также обратите внимание, что ваш дизайн API ограниченT:Copy
. Вам придется немного переделать его, если вы хотите поддержатьT: ?Copy + Clone
или T: ?Copy + ?Clone
.
охота за чертами
Компилятор - ваш друг. Посмотрите, что происходит, когда я удаляю одну из границ черты
error[E0277]: the trait bound `T: std::hash::Hash` is not satisfied
...
Использование макроса - интересное упражнение, но повторная реализация универсальных шаблонов с использованием макросов бесполезна.