Macro_rules type ascription

I have a pattern where calls I make to Tauri are essentially indirectly calling a separate library, so it saves dramatically on typing by just writing a macro to take the function, all of its arguments, and then write a function body that just simply calls the identically named function in my library:

macro_rules! call {
  (

      $( #[$meta:meta] )*
  //  ^~~~attributes~~~~^
      $vis:vis fn $name:ident ( $conn:ident : $state_type:ty, $( $arg_name:ident : $arg_ty:ty ),* $(,)? ),
  //                            ^~~~~~sqlite connection~~~~~^ ^~~~~~~~~~~~~~~~argument list~~~~~~~~~~~~~~^
      $namespace:tt,
      $return_result:ty
  ) => {
      $( #[$meta] )*
      $vis fn $name ( $conn : $state_type, $( $arg_name : $arg_ty ),* ) -> $return_result { toJson( $namespace::$name ( lock!($conn), $( $arg_name : $arg_ty ),* ) ) }
  }
}

so an example becomes:

call!(
  #[tauri::command]
  fn run_query(conn: Conn, name_id: i32),
  query,
  Result<String, String>
);

which writes for me:

#[tauri::command]
fn run_query(conn: Conn, name_id: i32) -> Result<String, String> {
  toJson(query::run_query(lock!(conn), name_id))  
}

where toJson serializes the final result and lock! locks the Mutex holding the sqlite connection

But this is unhappy because of type ascription on name_id. Is there a way to make this happy?:

error[E0658]: type ascription is experimental

Bonus: is there a way to not have to pass the return type as an argument? My lib returns Result<String,rusqlite:Error> that I then map to String for serialization. For instance, could it automagicallydetermine the generics, only replacingrusqlite:Errortype withString` instead?

1 Like

The macro expands to something like this:

fn run_query(conn: Conn, name_id: i32) -> Result<String, String> {
    toJson(query::run_query(conn, name_id: i32))
//                                       ^^^^^ - this is the ascription in question
}

The problematic part corresponds to : $arg_ty inside the macro - you probably want to simply drop it inside the call, the type is already defined by the signature.

It must be specified explicitly when defining the function - either being passed into the macro or hard-coded in the macro itself, it's impossible to infer it.

1 Like

Thanks! One small follow up: Do you know how to make the comma optional in cases where I just have 1 argument, like:

As a workaround, I can just write a trailing comma but it feels a little awkward fn hey(conn: i32,)

You could make the entire suffix optional:

macro_rules! call {
  (

      $( #[$meta:meta] )*
  //  ^~~~attributes~~~~^
      $vis:vis fn $name:ident ( $conn:ident : $state_type:ty $(, $( $arg_name:ident : $arg_ty:ty ),* $(,)? )? ),
  //                            ^~~~~~sqlite connection~~~^  ^~~~~~~~~~~~~~~~argument list~~~~~~~~~~~~~~~~~~^
      $namespace:tt,
      $return_result:ty
  ) => {
      $( #[$meta] )*
      $vis fn $name ( $conn : $state_type $(, $( $arg_name : $arg_ty ),* )? ) -> $return_result { toJson( $namespace::$name ( lock!($conn) $(, $( $arg_name ),* )? ) ) }
  }
}
1 Like