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
相关产品推荐
相关产品推荐

