Можно ли хранить все типы, реализующие черту, в списке и перебирать этот список? - PullRequest
0 голосов
/ 18 июня 2019

У меня есть черта, реализованная для многих структур (A, B, C и т. Д.):

pub trait ApplicableFor: Debug + Default {
    unsafe fn is_applicable_for(from: *mut u8) -> bool
    where
        Self: Sized;
}

Мне нужен метод, который находит, какая структура возвращает true для этого методавызов.У меня есть код:

unsafe fn check_applicable<T: ApplicableFor>(from: *mut u8) -> bool {
    T::is_applicable_for(from, to)
}

unsafe fn find_applicable(from: *mut u8) -> ApplicableFor {
    if check_applicable::<A>(from) {
        A::default()
    } else if check_applicable::<B>(from) {
        B::default()
    } else if check_applicable::<C>(from) {
        C::default()
    } else {
        panic!("Couldn't find appicable");
    }
}

В реальном коде у меня есть около 20 структур, поэтому я хочу где-то их хранить и использовать код для читабельности:

unsafe fn find_applicable(from: *mut u8) -> ApplicableFor {
    for T in list {
        if check_applicable::<T>(from) {
            T::default()
        }
    }
    panic!("Couldn't find appicable");
}

Какя могу сделать это или как я могу переписать это лучше?

1 Ответ

3 голосов
/ 18 июня 2019

Нет, Rust напрямую не предлагает необходимые вам функции метапрограммирования. А именно, тип не является конкретной вещью, которая существует или может быть помещена в коллекцию.

Вместо этого вам нужно генерация кода.

Начиная с упрощенной версии ApplicableFor, мы можем написать очень структурированную версию find_applicable:

trait ApplicableFor {
    fn is_applicable_for(from: u8) -> bool;
}

fn find_applicable(from: u8) {
    if <A>::is_applicable_for(from) {
        println!("Using {}", stringify!(A));
        return;
    }

    if <B>::is_applicable_for(from) {
        println!("Using {}", stringify!(B));
        return;
    }

    if <C>::is_applicable_for(from) {
        println!("Using {}", stringify!(C));
        return;
    }

    panic!("Couldn't find any applicable types");
}

Как только мы установили структуру, мы можем начать абстрагировать ее с помощью макросов:

fn find_applicable(from: u8) {
    macro_rules! find_one {
        ($ty:ty) => {
            if <$ty>::is_applicable_for(from) {
                println!("Using {}", stringify!($ty));
                return;
            }
        }
    }

    find_one!(A);
    find_one!(B);
    find_one!(C);

    panic!("Couldn't find any applicable types");
}

Что если мы хотим повторить эту концепцию «сделать что-то для этого списка типов»? Еще один макрос:

macro_rules! each_type {
    ($one_type_macro:tt) => {
        $one_type_macro!(A);
        $one_type_macro!(B);
        $one_type_macro!(C);
    };
}

fn find_applicable(from: u8) {
    macro_rules! find_one {
        ($ty:ty) => {
            if <$ty>::is_applicable_for(from) {
                println!("Using {}", stringify!($ty));
                return;
            }
        }
    }

    each_type!(find_one);

    panic!("Couldn't find any applicable types");
}

Слишком много шума для реализации each_type!? Создайте макрос, который создает другой макрос, который будет вызываться с другим макросом:

macro_rules! gen_each_type {
    ($($ty:ty),*) => {
        macro_rules! each_type {
            ($one_type_macro:tt) => {
                $($one_type_macro!($ty);)*
            };
        }
    };
}

gen_each_type![A, B, C];
...