简体   繁体   English

在 clap 中动态生成带有格式化描述的子命令

[英]Dynamically generate subcommands with formatted description in clap

I want to generate subcommands for clap based on a HashMap content.我想根据HashMap内容为clap生成子命令。 To do that, I'm iterating over the items to create new App instances.为此,我正在迭代这些项目以创建新的App实例。 The problem is that App.about() requires &str as an input, and I can't make one that lives long enough from the format!() macro.问题是App.about()需要&str作为输入,而我无法从format!()宏中创建一个寿命足够长的输入。

Here's an example ( playground ):这是一个示例( 操场):

use clap::App;
use std::collections::HashMap;

fn main() {
    let mut map = HashMap::new();
    map.insert("key", "value");
    map.insert("key2", "value2");
    map.insert("key3", "value3");

    let mut app = App::new("Test").version("1.0");

    for (key, value) in map.iter() {
        let about = format!("Value: {}", value);
        app = app.subcommand(App::new(*key)).about(about.as_str());
    }

    app.get_matches();
}

Error message:错误信息:

error[E0597]: `about` does not live long enough
  --> src/main.rs:14:52
   |
14 |         app = app.subcommand(App::new(*key)).about(about.as_str());
   |                                                    ^^^^^ borrowed value does not live long enough
15 |     }
   |     - `about` dropped here while still borrowed
16 | 
17 |     app.get_matches();
   |     --- borrow later used here

How can I pass the formatted string to the about() method?如何将格式化的字符串传递给about()方法?

First of all, it looks like you have an unrelated mistake, which I'll mention just to reduce confusion about code sample correctness.首先,看起来您有一个不相关的错误,我会提到它只是为了减少对代码示例正确性的混淆。 I haven't used clap myself, but it looks like this is overwriting the app's about repeatedly我没有用clap自己,但它看起来像这样被改写应用程序的about反复

app = app.subcommand(App::new(*key)).about(about.as_str());

and you probably meant你可能是说

app = app.subcommand(App::new(*key).about(about.as_str()));

to apply it to the subcommand instead.将其应用于子命令。


The problem here is that App doesn't want to own the strings, so there needs to be another object that does.这里的问题是App不想拥有这些字符串,所以需要有另一个对象来拥有。 One approach would be to construct a map that contains the format!一种方法是构建一个包含format!的地图format! ted strings (and thus owns them): ted 字符串(因此拥有它们):

    let abouts: HashMap<String, String> = map.iter()
        .map(|(key, value)| (key.to_string(), format!("Value: {}", value)))
        .collect();
    for (key, about) in abouts.iter() {
        app = app.subcommand(App::new(key.as_str()).about(about.as_str()));
    }

The map abouts must live as long as app .地图abouts必须与app一样长。

If you're only going to do this once in your program, then you can just leak the memory format!如果你只打算在你的程序中这样做一次,那么你可能会泄漏内存format! allocated:分配:

    for (key, value) in map.iter() {
        let about = &*Box::leak(format!("Value: {}", value).into_boxed_str());
        app = app.subcommand(App::new(*key).about(about));
    }

If you want to be able to reclaim the memory but don't want to have to deal with constructing a structure like abouts above, because your actual problem is more complex, you can use the typed_arena crate to provide a place to allocate any number of strings and keep them all alive:如果你希望能够回收内存,但不希望有处理类似构造的结构abouts之上,因为你实际的问题是比较复杂的,你可以使用typed_arena箱子提供一个地方分配任何数量的字符串并使它们保持活力:

use typed_arena::Arena;

...

    let arena = Arena::new();
    for (key, value) in map.iter() {
        let about = arena.alloc(format!("Value: {}", value)).as_str();
        app = app.subcommand(App::new(*key).about(about));
    }

Like in the abouts solution, you need to keep arena around as long as you keep app around.就像在abouts解决方案中一样,只要您保留app ,您就需要保留arena

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM