Клонировать структуру, хранящую замыкание
В настоящее время я пытаюсь реализовать простую библиотеку Parser-Combinator в Rust. Для этого я хотел бы иметь общий map
функция для преобразования результата парсера.
Проблема в том, что я не знаю, как скопировать структуру, содержащую замыкание. Примером является Map
struct в следующем примере. Оно имеет 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 ответ
Это возможно, если вы берете ссылку на закрытие, потому что вы можете 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())));
}