rust 强制在调用一个闭包之前调用另一个闭包

sgtfey8w  于 2023-03-23  发布在  其他
关注(0)|答案(1)|浏览(119)

我想把两个闭包捆绑在一个结构中。一个闭包是用来测试输入是否有效的,另一个闭包是用来实际做这些事情的。下面是一个可行的变体:

struct Foo<T, F>
{
    test: T,
    func: F,
}

fn make_foo<A, B, T, F>(test: T, func: F) -> Foo<T, F>
where
    T: Fn(A) -> bool,
    F: Fn(A) -> B
{
    Foo {
        test,
        func,
    }
}

fn main() {
    let foo = make_foo(|a: i32| {a > 5}, |a: i32| { a + 8 });
    if (foo.test)(10) {
        println!("{}", (foo.func)(10));
    }
    else {
        println!("Input too small");
    }
}

然而,Rust是Rust,有一些问题看起来不习惯。特别是,没有检查test是否实际运行,也没有强制testfunc使用相同的参数运行。
所以,我开始尝试修正这个问题。我的想法是foo.test返回一个Option<Fn>,其中Some变量包含func,但是我想使用的输入已经提供给它了。所以,作为初稿,类似于以下内容:

struct Foo<T>
{
    test: T
}

fn make_foo<A, B, T, F, H>(t: T, f: F) -> Foo<H>
where
    T: Fn(A) -> bool,
    F: Fn(A) -> B,
    H: Fn(A) -> Option<Fn() -> B>,
{
    Foo {
        test: |a: A| {
            if t(a) {
                Some(| | {f(a)})
            }
            else {
                None
            }
        }
    }
}

fn main() {
    let foo = make_foo(|a: i32| {a > 5}, |a: i32| { a + 8 });
    if let Some(f) = (foo.test)(10) {
        println!("{}", f());
    }
    else {
        println!("Input too small");
    }
}

这说明Fn() -> B需要dyn,这当然也意味着它在编译时有未知的大小。所以我们Box它:

fn make_foo<A, B, T, F, H>(t: T, f: F) -> Foo<H>
where
    T: Fn(A) -> bool,
    F: Fn(A) -> B,
    H: Fn(A) -> Option<Box<dyn Fn() -> B>>,
{
    Foo {
        test: |a: A| {
            if t(a) {
                Some(Box::new(| | {f(a)}))
            }
            else {
                None
            }
        }
    }
}

现在它抱怨说它在我写的闭包上有expected type parameter H , found closure,我真的不知道这是什么意思。它还抱怨说它现在想在main内部的foo上有一个显式的类型签名,考虑到该类型包括闭包,我认为我甚至不能这样做。还有,我甚至还没有接触到a参数。我可能需要一个move
我是不是在这里挖了一个无法脱身的洞,或者有什么像样的方法来解决这个问题,或者有什么更符合人体工程学的方法来解决我的代码运行中的两个问题?

cs7cruho

cs7cruho1#

可以使用existential type instead of an universal one

fn make_foo<A, B, T, F>(t: T, f: F) -> Foo<impl Fn(A) -> Option<Box<dyn Fn() -> B>>>
where
    A: Copy + 'static,
    T: Fn(A) -> bool,
    F: Fn(A) -> B + Copy + 'static,
{
    Foo {
        test: move |a: A| {
            if t(a) {
                Some(Box::new(move || {f(a)}) as _)
            }
            else {
                None
            }
        }
    }
}

一个类型参数总是意味着调用者可以为它选择一个类型,但是你不能提供一个任意的Fn(A) -> Option<Box<dyn Fn() -> B>>,你只能提供一个特定的值,你的闭包。

相关问题