Best practice of partially initialized parameters?

Currently, I'm parsing variables with clap, which implement parsing variables with macros.

Thus I may have a Arg type, which obtain original inputs from user input directly.

But some processes should be done, and some more fields might be calculated thus I should finally got a variable Args, which contains more fields than the original Arg.

What is the best practice to yield Args from Arg?

For example, I have

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Arg{
    #[arg(short, long, default_value = "")]
    name: String,
}

and

struct Args{
    name: String,
    nickname:String
}

I have mainly 3 ideas.

Firstly, using Options<T>, which initialize Args as Arg with None in all of its field that should be calculated:

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Arg{ // Actually it is also Args.
    #[arg(short, long, default_value = "")]
    name: String,
    #[arg(default_value_T = None)]
    nickname: Option<String>,
}
impl Arg {
    fn my_parse()->Self {
        let mut initial=Arg::parse();//this is a function generated by clap.
        initial.nick_name=Some(calc_nickname(&initial));
        initial
    }
}

Another 2 ideas are similar, the first one is wrapping the whole Arg type into Args:

struct Args {
    arg:Arg,
    nickname:String
}

and writedown all fields in Arg:

struct Args {
    name:String, // from Arg
    nickname:String
}

The second idea is not very directly since each time I obtain name from Args, I have to wrote args.arg.name rather than directly args.name.
The third idea is more directly, but I have to transform Arg to Args manually:

impl Args{
    fn from_arg(Arg:arg)->Self{
        let nickname=calc_nickname(&arg);
        Self{name:arg.name, nickname: nickname}
    }
}

Is there a better way to transform Arg to Args?

Or wrapping the whole Arg into Args is the best practice?

Of all of these possibilities, the one that is cleanest is the one where you convert Args to Arg. It is the only one that is not contaminating one aspect of the program with another.

I would generally say that you should choose that one, except when the program is so small that it wouldn't be worth the extra code.

Do give them clearer names though, like maybe CommandArgs and UserName or something.

3 Likes

To add a little to kpreid's answer: I do this with my interpreters/compilers written in Rust. So when I go from "syntax tree" to "typed tree", I'm translating one tree of types into a mostly identical tree of types, except these new types have more information in them.

It's a huge pile of code, but makes it very, very difficult to make mistakes around what information is available when.

... but honestly, in a case as simple as this, I'd probably just go with option #2 because it's simpler.

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.