-1

随着我继续学习 Rust,我正在从事一个涉及广泛使用谓词函数的项目。我决定用 Rust 闭包来实现这些谓词,例如:

type Predicate = Box<Fn(&Form) -> bool>.

我的程序使用应用于这些谓词的布尔逻辑。例如,两者and以及or都应用于这些谓词的值。我使用以下方法完成了这项工作Box::leak

struct Form {
    name: String,
}

fn and(a: Option<Predicate>, b: Option<Predicate>) -> Option<Predicate> {
    if a.is_none() {
        return b;
    } else if b.is_none() {
        return a;
    } else {
        let a = Box::leak(a.unwrap());
        let b = Box::leak(b.unwrap());
        return Some(Box::new(move |form: &Form| a(form) && b(form)));
    }
}

虽然这似乎可以按我Box::leak的意愿工作,但似乎并不理想。我不太了解std::rc::Rc并且std::cell::RefCell不知道这些是否可以帮助我避免Box::leak在这里使用它们可能需要对我的代码进行重大重组,但我想至少了解这里的惯用方法可能是什么。

有没有办法在保持相同功能的同时避免泄漏?

这是完整的示例

struct Form {
    name: String,
}

type Predicate = Box<Fn(&Form) -> bool>;

struct Foo {
    predicates: Vec<Predicate>,
}

impl Foo {
    fn and(a: Option<Predicate>, b: Option<Predicate>) -> Option<Predicate> {
        if a.is_none() {
            return b;
        } else if b.is_none() {
            return a;
        } else {
            let a = Box::leak(a.unwrap());
            let b = Box::leak(b.unwrap());
            return Some(Box::new(move |form: &Form| a(form) && b(form)));
        }
    }
}

fn main() {
    let pred = Foo::and(
        Some(Box::new(move |form: &Form| {
            form.name == String::from("bar")
        })),
        Some(Box::new(move |_: &Form| true)),
    )
    .unwrap();
    let foo = Foo {
        predicates: vec![pred],
    };
    let pred = &foo.predicates[0];
    let form_a = &Form {
        name: String::from("bar"),
    };
    let form_b = &Form {
        name: String::from("baz"),
    };
    assert_eq!(pred(form_a), true);
    assert_eq!(pred(form_b), false);
}
4

1 回答 1

4

您的代码不需要Box::leak,也不清楚您为什么认为它需要。如果代码被删除,代码将继续编译并具有相同的输出:

impl Foo {
    fn and(a: Option<Predicate>, b: Option<Predicate>) -> Option<Predicate> {
        if a.is_none() {
            b
        } else if b.is_none() {
            a
        } else {
            let a = a.unwrap();
            let b = b.unwrap();
            Some(Box::new(move |form: &Form| a(form) && b(form)))
        }
    }
}

unwraps 是非惯用的;一个更惯用的解决方案将使用match

impl Foo {
    fn and(a: Option<Predicate>, b: Option<Predicate>) -> Option<Predicate> {
        match (a, b) {
            (a, None) => a,
            (None, b) => b,
            (Some(a), Some(b)) => Some(Box::new(move |form| a(form) && b(form))),
        }
    }
}
于 2019-06-19T02:15:43.690 回答