Есть ли идиоматический способ избежать `Box :: leak` с этим кодом? - PullRequest
0 голосов
/ 19 июня 2019

Продолжая изучать 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);
}

1 Ответ

4 голосов
/ 19 июня 2019

Ваш код не нуждается в 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))),
        }
    }
}
...