如何将字面字符串与常量字符串连接?

22
我正在尝试实例化一个参数解析器(clap)。有以下代码:
const DEFAULT_VALUE: &'static str = "12312312";
// ...
.help("this parameter is for (default:" + DEFAULT_VALUE + ")")
// ...

我查看了类似的问题并发现了concat!宏和lazy_static。
第一个选项不适合,而且没有lazy_static的示例。 无论如何,如果可能的话,这将过于复杂,因为lazy_static需要在另一个位置定义块。
我正在寻找一些简洁的语法糖,在原地使用宏,不会有很多类型开销。
如果定义一个局部变量,它可能会变得太高,因为clap的DSL可能非常长。这不方便,因为它会从代码中的逻辑位置剥离字符串。
另一种方法是为整个帮助字符串定义静态变量,但它具有与上述方法相同的缺点,加上命名空间污染。
建议使用format!的解决方案也不适合。它需要定义一个局部变量。
示例
extern crate clap;

use clap::{Arg, App};

const DEFAULT: &'static str = "1";

fn main() {
    let params = App::new("app")
        .arg(Arg::with_name("p")
             // here 100 lines of the uninterruptable expression
             .help(&format!("parameter p (DEFAULT: {})", DEFAULT)))
             // here also 100 lines of the uninterruptable expression
        .get_matches();
    println!("param p = {}", params.value_of("p").unwrap())
}

Cargo.toml

[package]

name = "demo-clap"
version = "1.0.0"
[dependencies]

clap = "2.10.0"

编译错误

<std macros>:2:1: 2:61 error: borrowed value does not live long enough
<std macros>:2 $ crate :: fmt :: format ( format_args ! ( $ ( $ arg ) * ) ) )
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.rs:11:21: 11:66 note: in this expansion of format! (defined in <std macros>)
src/main.rs:13:24: 15:2 note: reference must be valid for the block suffix following statement 0 at 13:23...
src/main.rs:13         .get_matches();
                                      ^
src/main.rs:8:5: 13:24 note: ...but borrowed value is only valid for the statement at 8:4
src/main.rs:8     let params = App::new("app")
                  ^
src/main.rs:8:5: 13:24 help: consider using a `let` binding to increase its lifetime
src/main.rs:8     let params = App::new("app")
                  ^
error: aborting due to previous error
error: Could not compile `demo-clap`.

您的标题中写的是“contact”吗?是不是应该是“concat”呢? - Marc B
2个回答

15
如果您使用的是Rust 1.46.01或更高版本,请查看 (crates.io | docs.rs)上的const_format箱。
  • concatcp:将整数2布尔值&str常量连接成&'static str

  • formatcp: 类似于format的格式(发出&'static str);与concatcp使用相同的基元。

因此,对于您的示例,formatcp将提供最灵活的解决方案,不需要您提到的本地变量(假设您指的是在format!宏中从alloc::fmt::format得出的堆分配的字符串):

use clap::{Arg, App};  
use const_format::formatcp;  
  
const DEFAULT: &'static str = "1";  

fn main() {  
  let params = App::new("app")  
               .arg(Arg::with_name("p")  
                    .help(formatcp!("parameter p (DEFAULT: {})", DEFAULT)))  
               .get_matches();  
  println!("param p = {}", params.value_of("p").unwrap())  
}

运行app -h会得到以下结果:

app 

USAGE:
    app [p]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

ARGS:
    <p>    parameter p (DEFAULT: 1)

这个库中所有宏的限制:

  • 扩展为&'static str(即我提到的那些)的宏只接受来自具体类型的常量:
    • Type::<u8>::FOO OK ✅
    • Type::<TYPE_PARAMETER>::FOO BAD ❌
  • 整数参数需要约束(即使用字面值时必须添加i*/u*后缀)。
  • 不能在仅接受字符串文字的某些位置使用,即属性,这可能需要LiteralExpression
    • #[doc = "ab"] != #[doc = concatcp!("a", "b")]

1这是为了稳定的const fn改进所需的,它允许循环而不涉及std::mem::transmute丑陋的黑客行为
2更具体地说,这将涉及到所有的i*/u*原语。请注意,如果您传递字面量,则必须自行约束类型,使用所需的后缀。


11
你可以简单地使用引用和format!宏:
.help(&format!("this parameter is for (default: {})", DEFAULT_VALUE));

编辑:

在Rust语言中,你想要做的是不可能的

这是宏的一项根本限制,因为它们只处理各种标记。宏没有理解类型的能力,只有看起来像类型的标记。当concat!看到DESCRIPTION时,它只看到一个标识符,不知道它是一个字符串常量。但可以尝试使用某种字符串拼接const fn,因为它可以使用常量值创建新的常量,但这需要一些黑魔法。

相反地,您可以这样做:

macro_rules! DEFAULT {
    () => { "1" };
}

fn main() {
    let params = App::new("app")
        .arg(Arg::with_name("p")
             // here 100 lines of the uninterruptable expression
             .help(concat!("parameter p (DEFAULT: ", DEFAULT!(), ")")))
             // here also 100 lines of the uninterruptable expression
        .get_matches();
    println!("param p = {}", params.value_of("p").unwrap())
}

使用宏而不是常量,可以使用concat!宏。

这个不行。 <std宏>:2:1: 2:61 错误:借用的值存活时间不够长 <std宏>:2 $ crate :: fmt :: format ( format_args ! ( $ ( $ arg ) * ) ) ) - Daniil Iaitskov
你能否请提供一个 MCVE?从 clap 的文档中可以看到,一些 help 方法需要一个 &str,而另一些则需要 Into<&str>,因此也许在 format! 前面去掉 & 就能解决你的错误。 - antoyo
当然,但是我尝试去除 &,并没有重载的版本。问题不在于库中。这是一个更普遍的问题,即您在 DSL 中有一个方法参数需要 &str,并且您有一组文字字符串和常量要传递。 - Daniil Iaitskov
我编辑了我的回答,为您提供了实现您想要的功能的另一种方式。 - antoyo
有人知道关于merge request的情况吗?请求将一些AST转换实现为连接字面量和常量字符串,例如将多个字面量字符串连接在一起(就像在C中一样)。它们被拒绝了吗? - Daniil Iaitskov
1
我不确定,但是未来可能会使用const fn实现。 - antoyo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接