Help with idiomatic rust and ownership semantics

I think my problem is easy to understand. I am building a CLI with the clap library but I'd like to have the default values pull from a config file. To distill my problem, imagine I have

use clap::{App, Arg};

pub struct Config {
    pub foo: u16,
    pub bar: String,
}

pub fn mk_app<'a, 'b>(config: &'a Config) -> App<'a, 'b> {
    let foo = config.foo.to_string();
    App::new("myapp")
        .arg(
            Arg::with_name("bar")
                .takes_value(true)
                .default_value(&config.bar) // works fine 
        )
        .arg(
            Arg::with_name("foo")
                .takes_value(true)
                .default_value(&foo) // illegal
        )
}

So I'm passing in a reference to a configuration, since I imagine that would be more efficient, and because in my mind, mk_app really only needs to borrow the config for reading. (If this is not the right way to think about this, please tell me!)

This is illegal because, even though I've specified that parameter config and the resulting clap::App have the same lifetime (I think?), that is not true for the foo field of config that I had to convert to a string.

I've tried many permutations of this, and it makes sense to me that this should be illegal since the temporary string foo does get dropped at the end of this function. My question is, what is the idiomatic solution to this? How can create a reference to the string representation of config.foo with the proper lifetime?

Thanks for your help!

According to the doc, clap works with yaml files. It might not be exactly what you want but it would be easier.
Example and its yaml file.

To fix your lifetime issue I'd parse the u16 before calling mk_app.

I'm more interested in learning how to handle this situation more generally, so let's suppose I'm not using the clap bells and whistles; I just want to create some App<'a, 'b> datatype that ends up getting created by a few &'a str arguments (as documented here).

Your solution would be to do something like

let foo = config.foo.to_string();
app = mk_app(&config, &foo);

This kinda smells to me. For example, let's say I have 10 fields that need such conversion. Would idiomatic rust really be to parse all 10 fields into strings outside of mk_app, and pass in 10 arguments? Or if not 10 arguments, do I really need to have an intermediary ConfigOfAllStrings duplicated struct passed to this function? I imagine there is better way to do this.

I had in mind the ConfigOfAllStrings way with a From implementation.

Since this is all coming from a file you're probably going from one big str or String to typed data to Strings.
The middle step could be replaced by just validating the data but I'm not sure it'd be simpler.

I have no experience dealing with this specifically so someone else might have a very simple solution that I don't know.

Thanks for your help @leudz. FWIW your initial solution works fine, but of course that means I'm duplicating the let foo = config.foo.to_string(); calls whenever I want to call mk_app.

Your other solution to just keep them as strings an interesting idea, and might be the best solution actually. But, I'd definitely like to see methodology not specific to clap, because I can tell I'm missing some important aspect of how to deal with ownership semantics. So maybe it would be better to just consider some arbitrary App::new(arg1: &'a str, arg2: &'a str,...) call.

To recap, the issue is that a certain App struct requires &str references in its new constructor, and I'd like to organize my code such that I can create those &str values in the same function in which I create that App.

I think this is going to come down to "don't organize your code like this in Rust", I just need some hand-holding to see the correct way to go about this.

create those &str values in the same function in which I create that App .

Like you said in the first post, this is not possible, who would own the data?

The usual advice would be to make App own it but you can't modify it, that's why I focused so much on clap.

You generally don't want to deal with lifetimes too much, especially if you're not super familiar with ownership and borrowing, clone everything you need. But here it's not super helpful :sweat_smile:

2 Likes

Ohh okay. I think what I was missing was the fact that the "right" answer is to make App own it. And I just happen to be in this weird specific case where I'm constrained by the choices made by the external library that defines App. That makes sense. Thank you for your help!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.