Как реализовать макрос, который определяет новый общедоступный тип и возвращает экземпляр этого типа?

Я хочу реализовать структуру, используя 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
...

Использование макроса - интересное упражнение, но повторная реализация универсальных шаблонов с использованием макросов бесполезна.

Другие вопросы по тегам