Rust 闭包和 fn 不匹配

Rust closure and fn mismatch

我正在尝试做一些在函数式语言中很常见的事情,即 {F#、SML、OCaml、Haskel..} 将函数作为输入和输出传递。 但我认为在 Rust 类型系统方面我还没有放弃。下面的代码是我的代码中出错的部分。简而言之,我正在制作一个结构,该结构包含一个函数,该函数可以执行某些操作并 return 其他可能出错的操作。 or 函数有问题,因为我需要合并我在未来某个时间需要将输入传递到我正在使用闭包的 运行 函数。我试过使用 Fn 作为类型,但它仍然使编译器通过错误。

pub struct Pattern<T>{
   pat : fn(&str) -> Result<(T, &str),&'static str>
}

impl<T> Pattern<T> {
    pub fn run(self, input: &str) -> Result<(T,& str), &'static str> {
        (self.pat)(input)
    }

pub fn or(self, pat: Pattern<T>) -> Pattern<T> {
        Pattern {
            pat: |input| match self.run(input) {
                Ok(ret) => Ok(ret),
                Err(msg) => {
                    match pat.run(input) {
                        Ok(ret) => Ok(ret),
                        Err(msg) => Err(msg),
                    } 
                }
            }
        }  
    }
}

我不是extra-confident下面的解决方案,但谁知道...

首先,在 or() 中,您尝试提供生成的 Pattern 捕获 期望函数(无捕获)的闭包;正如你所说 在您的问题中,Fn 特征似乎更适合 pat 成员。 为了真正存储它,可以使用Box<dyn Fn( ...>

此外,run()or() 方法 消耗 Pattern,因此 每个 Pattern 只能使用一次,这可能不是您想要的。 我建议改为使用引用。

or中的结果Pattern按值返回(这是正确的, 在我看来);那么它的闭包需要捕获的所有局部变量 (selfpat 参数)必须 moved 进入闭包。

因为我们现在有很多 Pattern 互相提及, 我们必须明确处理生命周期。例如,两个 Pattern or() 中的组合必须比结果 Pattern 更有效。 这些约束在整个代码中传播。

最后尝试说明main()中的用法。

(可能更有经验的 Rust 程序员会发现一个更简单的 实现这个的方法,或者一些错误)

pub struct Pattern<'p, T> {
    pat: Box<dyn Fn(&str) -> Result<(T, &str), &'static str> + 'p>,
}

impl<'p, T> Pattern<'p, T> {
    pub fn run<'s>(
        &self,
        input: &'s str,
    ) -> Result<(T, &'s str), &'static str> {
        (self.pat)(input)
    }

    pub fn or<'a, 'b>(
        &'p self,
        pat: &'a Pattern<T>,
    ) -> Pattern<'b, T>
    where
        'p: 'b,
        'a: 'b,
    {
        Pattern {
            pat: Box::new(move |input| match self.run(input) {
                Ok(ret) => Ok(ret),
                Err(_msg) => match pat.run(input) {
                    Ok(ret) => Ok(ret),
                    Err(msg) => Err(msg),
                },
            }),
        }
    }
}

fn main() {
    let p1 = Pattern {
        pat: Box::new(|input| {
            if input.len() >= 4 {
                Ok((1, &input[0..4]))
            } else {
                Err("too short for p1")
            }
        }),
    };
    let p2 = Pattern {
        pat: Box::new(|input| {
            if input.len() >= 2 {
                Ok((2, &input[2..]))
            } else {
                Err("too short for p2")
            }
        }),
    };
    let p3 = p1.or(&p2);
    println!("p1: {:?}", p1.run("a"));
    println!("p2: {:?}", p2.run("a"));
    println!("p3: {:?}", p3.run("a"));
    println!("~~~~~~~~~~~~~~~~");
    println!("p1: {:?}", p1.run("abc"));
    println!("p2: {:?}", p2.run("abc"));
    println!("p3: {:?}", p3.run("abc"));
    println!("~~~~~~~~~~~~~~~~");
    println!("p1: {:?}", p1.run("abcdef"));
    println!("p2: {:?}", p2.run("abcdef"));
    println!("p3: {:?}", p3.run("abcdef"));

    /*
    p1: Err("too short for p1")
    p2: Err("too short for p2")
    p3: Err("too short for p2")
    ~~~~~~~~~~~~~~~~
    p1: Err("too short for p1")
    p2: Ok((2, "c"))
    p3: Ok((2, "c"))
    ~~~~~~~~~~~~~~~~
    p1: Ok((1, "abcd"))
    p2: Ok((2, "cdef"))
    p3: Ok((1, "abcd"))
    */
}