克隆存储闭包的结构体

10

我目前正在尝试在Rust中实现一个简单的解析器组合库。为此,我希望有一个通用的 map 函数来转换解析器的结果。

问题是我不知道如何复制一个包含闭包的结构体。以下示例中的 Map 结构体就是一个例子。它有一个 mapFunction 字段存储一个函数,该函数接收前一个解析器的结果并返回一个新的结果。 Map 本身是一个可以进一步与其他解析器组合的解析器。

但是,要使解析器可以组合,我需要它们能够被复制(具有 Clone 特性绑定),但我应该如何为 Map 提供这个特性呢?

示例代码:(仅伪代码,大概率无法编译)

trait Parser<A> { // Cannot have the ": Clone" bound because of `Map`.
    // Every parser needs to have a `run` function that takes the input as argument
    // and optionally produces a result and the remaining input.
    fn run(&self, input: ~str) -> Option<(A, ~str)>
}

struct Char {
    chr: char
}

impl Parser<char> for Char {
    // The char parser returns Some(char) if the first 
    fn run(&self, input: ~str) -> Option<(char, ~str)> {
        if input.len() > 0 && input[0] == self.chr {
            Some((self.chr, input.slice(1, input.len())))
        } else {
            None
        }
    }
}

struct Map<'a, A, B, PA> {
    parser: PA,
    mapFunction: 'a |result: A| -> B,
}

impl<'a, A, B, PA: Parser<A>> Parser<B> for Map<'a, A, B, PA> {
    fn run(&self, input: ~str) -> Option<(B, ~str)> {
        ...
    }
}

fn main() {
    let parser = Char{ chr: 'a' };
    let result = parser.run(~"abc");

    // let mapParser = parser.map(|c: char| atoi(c));

    assert!(result == Some('a'));
}

3
闭包不能被克隆。只要你使用闭包,我看不到任何解决困境的办法;看看能否使用其他东西,例如裸函数。 - Chris Morgan
2
使用裸函数是我目前的解决方法。但从客户的角度来看,我觉得它很丑陋。不过还是谢谢你的回答。 - akuendig
可能相关/有帮助的链接:https://dev59.com/_14c5IYBdhLWcg3weaP8请返回翻译后的文本。 - musicmatze
1个回答

1

如果您参考闭包,那么可以使用 Copy 引用来实现。

通常情况下无法克隆闭包。但是,您可以创建一个包含函数使用的变量的结构体类型,在其上派生 Clone,然后自己实现 Fn

闭包引用的示例:

// The parser type needs to be sized if we want to be able to make maps.
trait Parser<A>: Sized {
    // Cannot have the ": Clone" bound because of `Map`.
    // Every parser needs to have a `run` function that takes the input as argument
    // and optionally produces a result and the remaining input.
    fn run(&self, input: &str) -> Option<(A, String)>;

    fn map<B>(self, f: &Fn(A) -> B) -> Map<A, B, Self> {
        Map {
            parser: self,
            map_function: f,
        }
    }
}

struct Char {
    chr: char,
}

impl Parser<char> for Char {
    // These days it is more complicated than in 2014 to find the first
    // character of a string. I don't know how to easily return the subslice
    // that skips the first character. Returning a `String` is a wasteful way
    // to structure a parser in Rust.
    fn run(&self, input: &str) -> Option<(char, String)> {
        if !input.is_empty() {
            let mut chars = input.chars();
            let first: char = chars.next().unwrap();
            if input.len() > 0 && first == self.chr {
                let rest: String = chars.collect();
                Some((self.chr, rest))
            } else {
                None
            }
        } else {
            None
        }
    }
}

struct Map<'a, A: 'a, B: 'a, PA> {
    parser: PA,
    map_function: &'a Fn(A) -> B,
}

impl<'a, A, B, PA: Parser<A>> Parser<B> for Map<'a, A, B, PA> {
    fn run(&self, input: &str) -> Option<(B, String)> {
        let (a, rest) = self.parser.run(input)?;
        Some(((self.map_function)(a), rest))
    }
}

fn main() {
    let parser = Char { chr: '5' };
    let result_1 = parser.run(&"567");

    let base = 10;
    let closure = |c: char| c.to_digit(base).unwrap();

    assert!(result_1 == Some(('5', "67".to_string())));

    let map_parser = parser.map(&closure);
    let result_2 = map_parser.run(&"567");
    assert!(result_2 == Some((5, "67".to_string())));
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接