将字符串克隆到特定的生命周期

Clone String to Specific Lifetime

我目前正在尝试用 Rust 编写一个小的命令行应用程序,但我遇到了生命周期的瓶颈。

extern crate clap;
use self::clap::{App, Arg};
use std::env;

impl<'p> Params<'p> {
    fn get_username_arg<'r>() -> Arg<'r, 'r> {
        let mut arg = Arg::with_name("Username")
            .short("u")
            .long("username")
            .takes_value(true);
        match env::var("USERNAME") {
            Ok(username) => {
                // How do I pass `username` to default_value?
                arg.default_value(username)
            }
            Err(e) => arg.required(true),
        }
    }
    // More code below...
}

问题是我试图将 username 传递给默认值方法,该方法需要生命周期为 'rstr。我试过克隆,但我不知道如何告诉它克隆的生命周期是多少。我尝试了以下几行:

let cln = (&*username).clone::<'r>();
arg.default_value(username)

出于某种原因,它现在告诉我 username 的寿命不够长,尽管自从我克隆了数据后这应该无关紧要。

所以我的问题是,如何进行编译?

编辑:我想补充一点,除了生命周期参数之外,签名保持不变对我来说很重要。我不介意进行克隆等昂贵的操作来完成这项工作。

解释问题所在。

此处的解决方案是检索用户名(如果有),这样您就可以将其作为 String 参考。

let user_name = match env::var("USERNAME") {
    Ok(user_name) => Some( user_name ),
    Err(_) => None,
} ;
// Now we can take a reference on the user name String (if any) that can live
// long enough for the arg.
let arg = match user_name {
    Some(ref name) => arg.default_value(name),
    None => arg.required(true),
} ;

工作示例:

extern crate clap ;

use std::env;
use clap::* ;

fn main() {
    let arg = Arg::with_name("Username")
        .help("The user name")
        .short("u")
        .long("username")
        .takes_value(true);

    let user_name = match env::var("USERNAME") {
        Ok(user_name) => Some(user_name),
        Err(_) => None,
    };

    let arg = match user_name {
        Some(ref name) => arg.default_value(name),
        None => arg.required(true),
    };

    let app = App::new("Test").arg(arg);

    let matches = app.get_matches();

    match matches.value_of("username") {
        Some(name) => println!("name: \"{}\"", name),
        None => println!("no name :("),
    }
}

Arg::default_value 接受一个 &str 作为参数,这意味着字符串没有存储在 Arg 中,而是存储在其他地方。所以 &str 值必须比保持引用的 Arg 长。如果您使用从 get_username_arg 中创建的 String 值获得的 &strusername 就是这种情况),Arg 将比 &str(将存在于 get_username_arg 之外,而 &str 仅存在于 Ok 块中),因此这会产生编译器错误。

一个选项是将默认用户名作为参数传递:

extern crate clap;
use self::clap::Arg;
use std::env;

pub struct Params;

impl Params {
    fn get_username_arg(default: Option<&str>) -> Arg {
        let arg = Arg::with_name("Username")
            .short("u")
            .long("username")
            .takes_value(true);
        if let Some(d) = default {
            arg.default_value(d)
        } else {
            arg.required(true)
        }
    }
}

fn main() {
    // type can be omitted
    let username: Option<String> = env::var("USERNAME").ok();
    // username.as_ref() produces Option<&String>
    // map(String::as_str) produces Some(&str) from Some(&String)
    // or None from None
    let arg = Params::get_username_arg(username.as_ref().map(String::as_str));
}

注意 usernamearg 之前声明,所以 usernamearg 长。


I'd like to add that its important to me that the signature stays the same aside from the lifetime parameters. I don't mind doing expensive operations such as cloning to make this work.

您没有显示 Params 定义,但它似乎只是某些功能的 "name space"。如果是这种情况,您可以更改这些函数以接收 &self 作为参数(我知道这是在更改签名,但创建 args 的逻辑将保留在 Params 中),并存储 usernameParams 中:

extern crate clap;
use self::clap::Arg;
use std::env;

pub struct Params {
    username: Option<String>,
}

impl Params {
    fn new() -> Params {
        Params {
            username: env::var("USERNAME").ok(),
        }
    }

    fn get_username_arg(&self) -> Arg {
        let arg = Arg::with_name("Username")
            .short("u")
            .long("username")
            .takes_value(true);
        if let Some(d) = self.username.as_ref().map(String::as_str) {
            arg.default_value(d)
        } else {
            arg.required(true)
        }
    }
}

fn main() {
    let params = Params::new();
    let arg = params.get_username_arg();
}

,但我想讨论非工作代码的某些方面。

让我们从函数签名开始:

fn get_username_arg<'r>() -> Arg<'r, 'r> {

这表示 "for any lifetime that the caller of this function picks, I will return an Arg that contains references that will last that long"。这是一个很难坚持的承诺,因为调用者可以请求满足 'static 生命周期的东西,这个值比 main 的调用持续 长!事实上,履行 "any lifetime" 义务的唯一方法是 return 某事 'static

这是一个很好的迹象,表明将要出现问题。另见 ,它将这种情况显示为构造函数。许多人跳转到 return 和 String 以及 &str,所以这个答案也可能使这条道路短路。 ^_^

username doesn't live long enough, even though it shouldn't matter since I cloned the data.

username 有一个非常具体的生命周期,而且是有限的。如果您查看一段代码,通常可以直接找出对象的生命周期:它是变量在不移动的情况下所在的块的范围。在您的示例中,username 仅存在于匹配臂 Ok(username) => { // } 的块中。一旦该块退出,该值就会被销毁。

clone in this case has a signature of <'s>clone() -> &'s str if you remove elisions (and reify Self) according to my very limited understanding of Rust in general.

env::var returns a Result<String, VarError>,你访问 Ok 变体,使 username 成为 StringString implementation of clone 需要 &String,return 需要 String。我不确定 -> &'s str 的来源。

So if I clone with clone::<'r>() it should force the lifetime...

这是一个非常的常见错误。查看 (and maybe ) 了解一些背景信息。您 不能 改变某些东西的生命周期,除非重写您的代码以使引用的值具有更大的范围。生命周期语法反映了变量的生命周期,它不控制它。 "force" 一生没有(安全)方法。

(&*username).clone has that signature I mean

如果我们取消引用并重新引用 String,我们最终会得到 &str&str 的生命周期对应于 String 的生命周期。这是有道理的,因为 &str 只是指向 String。当 String 被释放时,&str 将指向不再处于有效状态的内存。