How to create a vector of all decorated functions from a specific module?

99封情书 提交于 2019-12-09 06:07:29

Rust does not have the same reflection features as Python. In particular, you cannot iterate through all functions of a module at runtime. At least you can't do that with builtin tools. It is possible to write so called procedural macros which let you add custom attributes to your functions, e.g. #[rule_decorator] fn foo() { ... }. With proc macros, you can do almost anything.

However, using proc macros for this is way too over-engineered (in my opinion). In your case, I would simply list all functions to be included in your vector:

fn test_constraint1(arg: u32) -> bool {
    arg < 29_500
} 
fn test_constraint2(arg: u32) -> bool {
    arg < 35_000
}

fn main() {
    let rules = vec![test_constraint1 as fn(_) -> _, test_constraint2];

    // Or, if you already have a vector and need to add to it:
    let mut rules = Vec::new();
    rules.extend_from_slice(
        &[test_constraint1 as fn(_) -> _, test_constraint2]
    );
}

A few notes about this code:

  • I replaced &Arg with u32, because it doesn't have anything to do with the problem. Please omit unnecessary details from questions on StackOverflow.
  • I used _ in the number literals to increase readability.
  • The strange as fn(_) -> _ cast is sadly necessary. You can read more about it in this question.

You can, with some tweaks and restrictions, achieve your goals. You'll need to use the inventory crate. This is limited to Linux, macOS and Windows at the moment.

You can then use inventory::submit to add values to a global registry, inventory::collect to build the registry, and inventory::iter to iterate over the registry.

Due to language restrictions, you cannot create a registry for values of a type that you do not own, which includes the raw function pointer. We will need to create a newtype called Predicate to use the crate:

use inventory; // 0.1.3

struct Predicate(fn(&u32) -> bool);
inventory::collect!(Predicate);

struct Rules;

impl Rules {
    fn validate_incomplete(&self, arg: &u32) -> bool {
        inventory::iter::<Predicate>
            .into_iter()
            .all(|Predicate(constraint)| constraint(arg))
    }
}

mod rules {
    use super::Predicate;

    pub fn test_constraint1(arg: &u32) -> bool {
        *arg < 29500
    }
    inventory::submit!(Predicate(test_constraint1));

    pub fn test_constraint2(arg: &u32) -> bool {
        *arg < 35000
    }
    inventory::submit!(Predicate(test_constraint2));
}

fn main() {
    if Rules.validate_incomplete(&42) {
        println!("valid");
    } else {
        println!("invalid");
    }
}

There are a few more steps you'd need to take to reach your originally-stated goal:

  • "a vector"

    You can collect from the provided iterator to build a Vec.

  • "decorated functions"

    You can write your own procedural macro that will call inventory::submit!(Predicate(my_function_name)); for you.

  • "from a specific module"

    You can add the module name into the Predicate struct and filter on that later.

See also:

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!