【问题标题】:How to pass a format! string and format! arguments to a Rust macro that creates a function?如何传递一个格式!字符串和格式!创建函数的 Rust 宏的参数?
【发布时间】:2023-01-18 00:29:38
【问题描述】:

我正在制作一个 Web API 客户端。我想创建对应于可用 API 端点的函数。

在某些情况下,URL 始终相同。然后,手动调用 API 如下所示:

let things_list_url = "https://example.com/api/things/list";
let things_list: Vec<SomeThing> = make_request(GET, thing_list_url).into();

我为此使用的宏如下所示:

macro_rules! api_request {
  ($name: ident, $method: ident, $path: expr, $return_type: ty) => {
    pub fn $name() -> $return_type {
      let action_url = format!("https://example.com/api{}", $path);
      let action_result = make_request($method, action_url);
      action_result.into()
    }
  };
}

api_request!(get_things_list, GET, "/things/list", Vec<SomeThing>);

fn main() {
  println!("{:?}", get_things_list());
}

只要 URL 保持不变,类似的模式适用于 API 调用有主体的情况。

其他一些端点在其 URL 中有参数。手动调用它们看起来像:

let thing = SomeThing { id: 123, ...};
let thing_status_url = format!("https://example.com/api/things/{}/status", thing.id);
let thing_status: SomeThingStatus = make_request(GET, thing_status_url).into();

但是,我为此制作宏的尝试不起作用。为简单起见,我们假设 format! 调用只有一个参数:

macro_rules! api_request_with_path {
  ($name: ident, $method: ident, $request_type: ty, $return_type: ty, $path_format_string: expr, $path_format_arg: expr) => {
    pub fn $name(
      arg: $request_type
    ) -> $return_type {
      let action_url_fragment = format!($path_format_string, $path_format_arg);
      let action_url = format!("https://example.com/api{}", action_url_fragment);
      let action_result = make_request($method, action_url);
      action_result.into()
    }
  };
}

api_request_with_path!(get_thing_status, GET, SomeThing, SomeThingStatus, "things/{}/status", arg.id);

这失败了,因为我正在传递一个包含 arg 的表达式——生成函数的参数——但是 arg 在宏调用所在的范围内不存在。 我怎样才能为宏提供一种将 $request_type 类型的参数转换为 URL 字符串的方法?

【问题讨论】:

    标签: rust macros


    【解决方案1】:
    1. 您可以将部分表达式作为原始标记序列传入
    2. 您可能应该接受格式字符串的文字而不是表达式
      macro_rules! api_request_with_path {
        ($name: ident, $method: ident, $request_type: ty, $return_type: ty, $path_format_string: literal, $($path_format_arg: tt)*) => {
          pub fn $name(
            arg: $request_type
          ) -> $return_type {
            let action_url_fragment = format!($path_format_string, arg $($path_format_arg)*);
            let action_url = format!("https://example.com/api{}", action_url_fragment);
            let action_result = make_request($method, action_url);
            action_result.into()
          }
        };
      }
      
      api_request_with_path!(get_thing_status, GET, SomeThing, SomeThingStatus, "things/{}/status", .id);
      

      如果这不够灵活,您可以改为接受闭包

      macro_rules! api_request_with_path {
        ($name: ident, $method: ident, $request_type: ty, $return_type: ty, $path_format_string: literal, $path_format_arg: expr) => {
          pub fn $name(
            arg: $request_type
          ) -> $return_type {
            let action_url_fragment = format!($path_format_string, $path_format_arg(arg));
            let action_url = format!("https://example.com/api{}", action_url_fragment);
            let action_result = make_request($method, action_url);
            action_result.into()
          }
        };
      }
      
      api_request_with_path!(get_thing_status, GET, SomeThing, SomeThingStatus, "things/{}/status", |arg: SomeThing| arg.id);
      

      如果你想避免需要指定闭包参数类型,你可以标记闭包:

      macro_rules! api_request_with_path {
        ($name: ident, $method: ident, $request_type: ty, $return_type: ty, $path_format_string: literal, |$path_format_arg_var: ident| $($path_format_arg: tt)*) => {
          pub fn $name(
            arg: $request_type
          ) -> $return_type {
            let closure = |$path_format_arg_var: $request_type| $($path_format_arg)*;
      
            let action_url_fragment = format!($path_format_string, closure(arg));
            let action_url = format!("https://example.com/api{}", action_url_fragment);
            let action_result = make_request($method, action_url);
            action_result.into()
          }
        };
      }
      
      api_request_with_path!(get_thing_status, GET, SomeThing, SomeThingStatus, "things/{}/status", |arg| arg.id);
      

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-06-10
      • 1970-01-01
      • 2021-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-26
      相关资源
      最近更新 更多