如何从特定模块创建包含所有修饰功能的向量?

时间:2019-04-13 08:53:37

标签: module rust decorator

我有一个文件main.rs和一个文件rule.rs。我想在rule.rs中定义要包含在Rules::rule向量中的函数,而不必一一推送。我希望有一个循环来推动它们。

main.rs

struct Rules {
    rule: Vec<fn(arg: &Arg) -> bool>,
}

impl Rules {
    fn validate_incomplete(self, arg: &Arg) -> bool {
        // iterate through all constraints and evaluate, if false return and stop
        for constraint in self.incomplete_rule_constraints.iter() {
            if !constraint(&arg) {
                return false;
            }
        }
        true
    }
}

rule.rs

pub fn test_constraint1(arg: &Arg) -> bool {
    arg.last_element().total() < 29500
}

pub fn test_constraint2(arg: &Arg) -> bool {
    arg.last_element().total() < 35000
}

Rules::rule应填充test_constraint1test_constraint2

在Python中,我可以在要包含在@rule_decorator中的约束之上添加装饰器Vec,但在Rust中看不到装饰器。

在Python中,我可以使用dir(module)查看所有可用的方法/属性。

Python变体:

class Rules:

    def __init__(self, name: str):
        self.name = name
        self.rule = []

        for member in dir(self):
            method = getattr(self, member)
            if "rule_decorator" in dir(method):
                self.rule.append(method)

    def validate_incomplete(self, arg: Arg):
        for constraint in self.incomplete_rule_constraints:
            if not constraint(arg):
                return False
        return True

使用rule.py文件:

@rule_decorator
def test_constraint1(arg: Arg):
    return arg.last_element().total() < 29500

@rule_decorator
def test_constraint1(arg: Arg):
    return arg.last_element().total() < 35000

所有带有rule_decorator的函数都将添加到self.rule列表中,并由validate_incomplete函数选中。

2 个答案:

答案 0 :(得分:0)

Rust没有与Python相同的反射功能。特别是,您不能在运行时遍历模块的所有功能。至少您不能使用内置工具来做到这一点。可以编写所谓的过程宏,使您可以向函数中添加自定义属性,例如#[rule_decorator] fn foo() { ... }。使用proc宏,您几乎可以执行任何操作。

但是,为此,使用proc宏的方法过于工程化(我认为)。就您而言,我只列出所有要包含在向量中的函数:

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]
    );
}

有关此代码的一些注意事项:

  • 我将&Arg替换为u32,因为它与问题无关。请忽略有关StackOverflow问题的不必要的详细信息。
  • 我在数字文字中使用了_以提高可读性。
  • 奇怪的是as fn(_) -> _强制转换是很必要的。您可以在this question中阅读有关它的更多信息。

答案 1 :(得分:0)

您可以通过一些调整和限制来实现您的目标。您需要使用inventory crate。目前仅限于Linux,macOS和Windows

然后,您可以使用inventory::submit将值添加到全局注册表,inventory::collect来构建注册表,并使用inventory::iter来遍历注册表。

由于语言限制,您无法为不属于您的类型的值(包括原始函数指针)创建注册表。我们将需要创建一个名为Predicate newtype 来使用板条箱:

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");
    }
}

要实现最初设定的目标,您还需要采取一些其他步骤:

  • “向量”

    您可以从提供的迭代器中collect来构建Vec

  • “装饰函数”

    您可以编写自己的程序宏,该宏将为您调用inventory::submit!(Predicate(my_function_name));

  • “来自特定模块”

    您可以将the module name添加到Predicate结构中并稍后对其进行过滤。

另请参阅: