rustmacros

.expect( format!() ): expected `&str`, found struct `String`


I tried to create a macro that replaces,

first: Some(first.as_ref().parse::<u64>().expect("'first' should be an unsigned integer"))

I've seen this done in other modules like Clap with values_t!, my attempt to abstract this doesn't extend to types. I wrote this,

macro_rules! parse_u64 {
  ($var:ident) => {
    Some(
      $var
      .as_ref()
      .parse::<u64>()
      .expect(  format!("'{:?}' should be an unsigned integer", stringify!($var))  )
    ) 
  };
}

This produces the following error,

first: parse_u64!(first),
       ^^^^^^^^^^^^^^^^^ expected `&str`, found struct `String`

What am I doing wrong here: this is a simple macro with only three things in the chain? Why do I get this error and how can I fix it?


You can see this in this Rust Playground example


Solution

  • As you mentioned in your own answer, expect takes a &str so you can't pass in a String directly:

    .expect(format!("'{:?}' should be an unsigned integer", stringify!($var)).as_str())
    

    However, this will perform string formatting (and allocate a new string) even if there was no error, so instead I would propose this:

    .unwrap_or_else(|err| panic!("'{:?}' should be an unsigned integer: {}", stringify!($var), err))
    

    This is effectively the same as the above, expect that unwrap_or_else only invokes its closure in the error case.


    Alternatively, in this specific case you can concatenate string literals at compile-time with concat!, which also avoids the cost of format!:

    .expect(concat!("'", stringify!($var), "' should be an unsigned integer"))