Предположим, я хочу создать комбинатор, который несколько раз использует другой синтаксический анализатор, например, для анализа строки, разделенной двумя типами кавычек:
fn quoted<'a, F: 'a, O, E: ParseError<&'a str>>(f: F) -> impl Fn(&'a str) -> IResult<&'a str, O, E>
where
F: Fn(&'a str) -> IResult<&'a str, O, E>,
{
map(
alt((
tuple((tag("'"), f, tag("'"))),
tuple((tag("\""), f, tag("\"")))
)),
|(_, res, _)| res,
)
}
Этот синтаксический анализатор, как и ожидалось, не скомпилируется сошибка «использования перемещенного значения»:
149 | fn quoted<'a, F: 'a, O, E: ParseError<&'a str>>(f: F) -> impl Fn(&'a str) -> IResult<&'a str, O, E>
| - - move occurs because `f` has type `F`, which does not implement the `Copy` trait
| |
| consider adding a `Copy` constraint to this type argument
...
155 | tuple((tag("'"), f, tag("'"))),
| - value moved here
156 | tuple((tag("\""), f, tag("\"")))
| ^ value used here after move
Однако я не могу просто добавить Copy
или Clone
к F
границам: множество парсеров, в частности, возвращаютсяВстроенные функции Nom не реализуют ни Clone
, ни Copy
.Я также не могу использовать &f
в качестве аргумента для tuple
, потому что тогда это будет ошибкой проверки заимствования (f
- это временное локальное значение, поэтому невозможно вернуть созданный с ним анализатор).
Единственный способ, которым я вижу это, - это на самом деле переопределить логику alt
непосредственно в функции, развернув ее в последовательности вложенных операторов match
, но это кажется действительно неоптимальным.Или, может быть, я упускаю что-то простое, и на самом деле можно делать то, что я хочу, используя только комбинаторы?
Я почти уверен, что есть лучший способ написать именно комбинатор quoted
, как описановыше, и было бы неплохо, если бы кто-то показал это, но мой вопрос более общий: как мне написать комбинаторы, которые повторно используют один и тот же парсер?