Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clone String to Specific Lifetime

I'm currently trying to write a little command line app in Rust and I've hit a wall with lifetimes.

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...
}

The problem is that I'm trying to pass username to the default value method, which requires a str with a lifetime of 'r. I tried cloning but I can't figure how to tell it what the lifetime of the clone is going to be. I tried something along the following lines:

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

For some reason its now telling me that username doesn't live long enough, even though it shouldn't matter since I cloned the data.

So my question is, how do I make this compile?

EDIT: 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.

like image 454
Jonathan Boudreau Avatar asked May 28 '16 18:05

Jonathan Boudreau


People also ask

What is the use of string clone () method?

String.Clone () method is used to get a reference to the given instance of String. In this tutorial, we will learn about the syntax of C# String.Clone () method, and learn how to use this method with the help of examples. String.Clone () returns a reference to this instance of String. The syntax of Clone () method is

What is the use of clone () in C #?

Introduction to clone () in C#. Clone () in C# is a method of string that is used to return the exact copy of an object. It returns the instance of the string. The return is just the copy with a different view. This method is also useful if we want to clone an array. In the case of the array, it creates a copy of the array with the same number ...

What is the syntax of clone () method in Java?

The syntax of Clone () method is This method returns an value of type Object. We may have to typecast it to String. In this example, we will take a string str and clone it using String.Clone () method. In this example, we will take a string str with null value, and clone it using String.Clone () method.

How to change the value of an array After cloning it?

The clone () method is used to clone those elements. We can also change the value of any element of that cloned array. In the output, first, it is displaying the given array and the cloned array. We can also change the value by passing its indexing position.


Video Answer


2 Answers

malbarbo has provided some good solutions, but I'd like to discuss some aspects of the non-working code.

Let's start at the function signature:

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

This says "for any lifetime that the caller of this function picks, I will return an Arg that contains references that will last that long". That's a pretty difficult promise to uphold, as the caller could request something that meets the 'static lifetime, a value that lasts longer than the call to main! In fact, the only way that you could meet the obligation of "any lifetime" is to return something that is 'static.

This is a very good sign that there is going to be a problem. See also Why can't I store a value and a reference to that value in the same struct?, which shows this case as a constructor. Many people jump to attempting to return the String along with the &str, so that answer might short-circuit that avenue as well. ^_^

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

username has a very specific lifetime and it's finite. If you look at a piece of code, it's generally straight-forward to find out the lifetime of an object: it's the scope of the block that the variable lives in without moving. In your example, username only lives during the block that's part of the match arm Ok(username) => { // }. As soon as that block exits, the value is destroyed.

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>, and you access the Ok variant, making username a String. The String implementation of clone takes a &String and returns a String. I'm not sure where the -> &'s str would come from.

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

This is a very common mistake. Check out Do Rust lifetimes influence the semantics of the compiled program? (and maybe Why are explicit lifetimes needed in Rust?) for some background information. You cannot change the lifetime of something other than by rewriting your code to make the referred-to value have a larger scope. The lifetime syntax reflects how long the variable lives, it does not control it. There's no (safe) way to "force" a lifetime.

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

If we dereference and re-reference a String, we end up with a &str. That &str will have a lifetime that corresponds to how long the String lives. This makes sense because the &str is just pointing to the String. When the String is deallocated, the &str would be pointing at memory that is no longer in a valid state.

like image 165
Shepmaster Avatar answered Sep 22 '22 05:09

Shepmaster


Arg::default_value takes a &str as parameter, this means that the string is not stored in Arg, it is stored somewhere else. So the &str value must outlive the Arg that keeps the reference. If you use a &str obtained from a String value created in get_username_arg (that's the case for username), the Arg will outlive the &str (will live outside the get_username_arg while the &str lives only in the Ok block), so this generates a compiler error.

One option is to pass the default username as a parameter:

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));
}

Note that username is declared before arg, so username outlives arg.


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.

You do not show the Params definition, but it seems that it is just a "name space" for some functions. If this is the case, you can change these functions to receive &self as parameter (I know this is changing the signature, but the logic for creating args will stay in Params), and store username in Params:

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();
}
like image 24
malbarbo Avatar answered Sep 22 '22 05:09

malbarbo