You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust宏报错Expected literal:concat!生成路径不满足export_to字面量要求

问题

我正在学习Rust宏,尝试用它将内容导出到指定目录。ts属性的export_to参数要求传入字符串字面量,原本以为concat!宏可以生成符合要求的路径,却触发了Expected literal错误。观察宏展开似乎正常,不清楚这是宏的限制还是有解决办法。

代码示例

#[macro_export]
macro_rules! create_endpoint {
    ($route:expr, $method:expr, $in_t:ty, $out_t:ty ) => {
        #[derive(Clone, Debug, Serialize, Deserialize, TS)]
        #[ts(export, export_to = concat!("../src/lib/endpoint_defines/", $route, "/", $method, ".ts"))]
        struct __Endpoint {
          in_type: $in_t,
          out_data_type: $out_t
        }
        
        #[cfg(test)]
        #[test]
        fn __export_write_endpoint() {
          write_endpoint($method, $route);
        }
    };
}

错误信息

error: expected literal
  --> src/handshake/endpoint.rs:13:34
   |
13 |         #[ts(export, export_to = concat!("../src/lib/endpoint_defines/", $route, "/", $method, ".ts"))]
   |                                  ^^^^^^
   |
  ::: src/routes/mod.rs:18:1
   |
18 | create_endpoint!("", "POST", (), ());
   | ------------------------------------ in this macro invocation
   |
   = note: this error originates in the macro `create_endpoint` (in Nightly builds, run with -Z macro-backtrace for more info)

解决方案

问题根源在于属性宏的执行时机:ts这类属性宏在编译早期阶段运行,只能识别直接的字符串字面量,而concat!是普通的展开式宏,要到后续阶段才会被求值,属性宏无法解析concat!(...)这个调用表达式。

解决思路是在create_endpoint!宏内部提前完成字符串拼接,生成真正的字面量再传给ts属性,具体有两种可靠方式:

方式一:利用宏的字面量自动拼接特性

将宏的$route和$method参数限定为literal类型(确保传入的是字符串字面量),然后在属性中直接拼接多个字符串片段,macro_rules!会自动将相邻的字符串字面量合并为一个完整的字面量:

#[macro_export]
macro_rules! create_endpoint {
    // 限定参数为字符串字面量类型
    ($route:literal, $method:literal, $in_t:ty, $out_t:ty ) => {
        #[derive(Clone, Debug, Serialize, Deserialize, TS)]
        // 直接拼接生成完整路径字面量
        #[ts(export, export_to = "../src/lib/endpoint_defines/" $route "/" $method ".ts")]
        struct __Endpoint {
          in_type: $in_t,
          out_data_type: $out_t
        }
        
        #[cfg(test)]
        #[test]
        fn __export_write_endpoint() {
          write_endpoint($method, $route);
        }
    };
}

方式二:借助常量存储拼接后的路径

先在宏内部用concat!生成常量字符串,再将常量传给ts属性(部分属性宏支持常量作为参数):

#[macro_export]
macro_rules! create_endpoint {
    ($route:expr, $method:expr, $in_t:ty, $out_t:ty ) => {
        // 提前用concat!生成完整路径常量
        const ENDPOINT_EXPORT_PATH: &str = concat!(
            "../src/lib/endpoint_defines/",
            $route,
            "/",
            $method,
            ".ts"
        );
        
        #[derive(Clone, Debug, Serialize, Deserialize, TS)]
        #[ts(export, export_to = ENDPOINT_EXPORT_PATH)]
        struct __Endpoint {
          in_type: $in_t,
          out_data_type: $out_t
        }
        
        #[cfg(test)]
        #[test]
        fn __export_write_endpoint() {
          write_endpoint($method, $route);
        }
    };
}

两种方式都能让ts属性拿到符合要求的字符串字面量,解决Expected literal错误。

内容的提问来源于stack exchange,提问作者Eatenpancreas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 20:34:51