是否有一种惯用的方法可以避免使用此代码产生`Box :: leak`?

时间:2019-06-19 00:04:49

标签: memory-leaks rust

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

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

我的程序使用应用于这些谓词的布尔逻辑。例如,andor都应用于这些谓词的值。我使用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::Rcstd::cell::RefCell不太了解,是否可以帮助我避免在这里使用Box::leak-使用它们可能需要对我的代码进行重大的重组,但我想最不了解这里的惯用方法。

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

这里是the complete example

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

1 个答案:

答案 0 :(得分: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)))
        }
    }
}

unwrap是非惯用语;更加惯用的解决方案将使用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))),
        }
    }
}