Trouble creating static struct

Hi, I'm writing a parser library using combinators. There's a parser combinator (a function) called sequence that takes many arguments so I extracted them into a struct that looks like this:

#[derive(Clone)]
struct SequenceArgs<'a, A: 'a, S: 'a> {
  start: &'static str,
  separator: &'static str,
  end: &'static str,
  spaces: BoxedParser<'a, (), S>,
  item: BoxedParser<'a, A, S>,
  trailing: Trailing,
}
#[derive(Clone)]
enum Trailing {
  Forbidden,
  Optional,
  Mandatory,
}

In the sequence function,

fn sequence<'a, A: Clone + 'a, S: Clone + 'a>(args: &'a SequenceArgs<'a, A, S>) -> BoxedParser<'a, Vec<A>, S>

I want to pass a reference called args as argument. Only reference seems to work because closures inside the function required moving args into them while the SequenceArgs struct doesn't implement Copy. The function definition worked but I struggled to find a way to create a static SequenceArgs struct to call it.

let args = SequenceArgs {
    start: "[",
    end: "]",
    separator: ",",
    spaces: space0(),
    item: token("abc"),
    trailing: Trailing::Optional,
  };
  assert_eq!(
    success(sequence(&args), "[ abc, abc, abc ]"), //!!!: &args doesn't live long enough
    vec!["abc", "abc", "abc"]
  )

The compiler complained that the borrowed value args doesn't live long enough.

const doesn't work because function calls like space0() are not allowed.

static doesn't work either because I used dynamic trait objects and Rc which is not thread-safe.

So, how can I create a static struct and pass it as argument?

Link to relevant code in my repo.

What happens if you do this?

fn sequence<'a, 'b, A: Clone + 'a, S: Clone + 'a>(
    args: &'b SequenceArgs<'a, A, S>
) -> BoxedParser<'a, Vec<A>, S>

If it doesn't work, please post the error message.

2 Likes

This unfortunately results in a lifetime mismatch in a chain! macro I called:

error[E0623]: lifetime mismatch
    --> src/lib.rs:978:19
     |
978  |       $first_parser.and_then(move | output |
     |                     ^^^^^^^^ ...but data from `args` flows into `args` here
...
1192 |   fn sequence<'a, 'b, A: Clone + 'a, S: Clone + 'a>(args: &'b SequenceArgs<'a, A, S>) -> BoxedParser<'a, Vec<A>, S> {
     |                                                           --------------------------
     |                                                           |
     |                                                           these two types are declared with different lifetimes...
...
1198 | /       chain!(
1199 | |         args.item.clone(),
1200 | |         zero_or_more(right(wrap(args.spaces.clone(), token(args.separator), args.spaces.clone()), args.item.clone()))
1201 | |       ).map(move |(first_item, mut rest_items)| {
     | |_______- in this macro invocation
error[E0623]: lifetime mismatch
    --> src/lib.rs:978:19
     |
978  |       $first_parser.and_then(move | output |
     |                     ^^^^^^^^ ...but data from `args` flows into `args` here
...
1192 |   fn sequence<'a, 'b, A: Clone + 'a, S: Clone + 'a>(args: &'b SequenceArgs<'a, A, S>) -> BoxedParser<'a, Vec<A>, S> {
     |                                                           --------------------------
     |                                                           |
     |                                                           these two types are declared with different lifetimes...
...
1198 | /       chain!(
1199 | |         args.item.clone(),
1200 | |         zero_or_more(right(wrap(args.spaces.clone(), token(args.separator), args.spaces.clone()), args.item.clone()))
1201 | |       ).map(move |(first_item, mut rest_items)| {
     | |_______- in this macro invocation

All combinators I used seem to require explicit 'a lifetime for all parameters, for example:

fn and_then<F, NextParser, NewOutput>(self, f: F) -> BoxedParser<'a, NewOutput, State>
  where
    Self: Sized + 'a,
    Output: 'a,
    NewOutput: 'a,
    State: 'a,
    NextParser: Parser<'a, NewOutput, State> + 'a,
    F: Fn(Output) -> NextParser + 'a,
  {
      BoxedParser::new(and_then(self, f))
  }

Without macro, my current solution is to still use normal positional arguments.

pub fn sequence<'a, A: Clone + 'a, S: Clone + 'a>(
  start: &'static str,
  item: BoxedParser<'a, A, S>,
  separator: &'static str,
  spaces: BoxedParser<'a, (), S>,
  end: &'static str,
  trailing: Trailing
) -> BoxedParser<'a, Vec<A>, S>

This will be improved if anonymous struct or named argument come out in Rust.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.