克隆存储闭包的结构

时间:2014-02-21 11:58:35

标签: rust rust-0.9

我目前正在尝试在Rust中实现一个简单的Parser-Combinator库。为此,我希望有一个通用的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'));
}

1 个答案:

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