10

I'm currently trying to implement a simple Parser-Combinator library in Rust. For that I would like to have a generic map function to transform the result of a parser.

The problem is that I don't know how to copy a struct holding a closure. An example is the Map struct in the following example. It has a mapFunction field storing a function, which receives the result of the previous parser and returns a new result. Map is itself a parser that can be further combined with other parsers.

However, for parsers to be combined I would need them to be copyable (having the Clone trait bound), but how do I provide this for Map?

Example: (Only pseudocode, will most likely not compile)

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'));
}
Jan Hohenheim
  • 3,552
  • 2
  • 17
  • 42
akuendig
  • 101
  • 3
  • 3
    Closures can't be cloned. I can't see any way out of your predicament while you use closures; see if you can use something else, e.g. bare functions. – Chris Morgan Feb 21 '14 at 12:55
  • 2
    Using bare functions is my current workaround. I just find it ugly from a clients point of view. Still, thanks for answering. – akuendig Feb 21 '14 at 18:16
  • Could be related / of help: https://stackoverflow.com/questions/27883509/can-you-clone-a-closure – musicmatze Feb 04 '17 at 14:54

1 Answers1

1

It's possible if you take a reference to the closure because you can Copy references.

Cloning closures is not possible in general. However, you can make a struct type that contains the variables that the function uses, derive Clone on it, and then implement Fn on it yourself.

Example of a reference to a closure:

// 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())));
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Bram Geron
  • 273
  • 1
  • 12