Clap: Just how eager are the eager options

Hi,

I've got this visual artifact while running my app:

~/dir-src5 ᐅ procrustes --help
 🌑 procrustes v1.0.3
...
Normal help output here...

This black circle comes from the spinner, which starts first thing inside the main() function:

fn main() {
    let mut g = GlobalState {
        spinner: Spinner::new(),

Small fry, of course, but. Is there a way to guarantee that nothing from the main() will run before the help?

How do you do the argument parsing? Maybe you could show us some more of your main() function. The main() function is the entry point for your program, it is the function that does the argument parsing, too, in the first place, as well as displaying the help message; so to “guarantee that nothing from the main() will run before the help” does – technically – not make too much sense. Depending on how you do argument parsing, it might be straightforward to make that happen before you create the GlobalState with its Spinner.

Ah, yes, something interesting here. Done a couple of years ago :slight_smile:

Global scope:

lazy_static! {
    static ref ARGS: ArgMatches<'static> = args_retrieve();
...
}

...

fn args_retrieve() -> ArgMatches<'static> {
    App::new("procrustes")
        .setting(AppSettings::ColoredHelp)
        .version("v1.0.3")
        .author("")
        .about(APP_DESCRIPTION)
        .arg(
            Arg::with_name("v")
                .short("v")
                .long("verbose")
                .help("Verbose output"),
        )
...

Nothing interesting in the main() function, trust me. Do I have to rewrite this, and how? ARGS is used everywhere, of course.

You should probably not do that :

lazy_static! {
    static ref ARGS: ArgMatches<'static> = args_retrieve();
...
}

And instead, around the first line of your main do :

fn main() {
    let args = args_retrieve();
    ...
}

And explicitely pass that to the functions which need it. That was also why your tests failed a few days ago.

For a minimal change, just make the first line of main be

let _ = *ARGS; // make sure arguments are handled at this point

I guess?

Eh? Looks interesting, but what is it?

Dereferencing the lazy_static to make sure its creation (and hence the help message being displayed and the program exiting) happens before anything else in main.

Note the documentation of lazy_static

For a given static ref NAME: TYPE = EXPR;, the macro generates a unique type that implements Deref<TYPE> and stores it in a static with name NAME . (Attributes end up attaching to this type.)

On first deref, EXPR gets evaluated and stored internally, such that all further derefs can return a reference to the same object.

(emphasis mine)

You are suggesting total rewrite. Besides, ARGS is read only, treated like a constant. It is not bad style to use a constant throughout your code. I'd like to get away with it :slight_smile:

Well, yes. But, from what I've seen you've already burned yourself (at least) twice with the issue of a global variable. So you should probably learn to avoid them where possible, and here the best practice would not be to parse arguments globally.

Sorry, I'm not getting it so fast. I tried your suggestion literally:

fn main() {
    *ARGS; // make sure `ARGS` is initialized after this point

    let mut g = GlobalState {
        spinner: Spinner::new(),
...

Got an error:

error[E0507]: cannot move out of dereference of `ARGS`
   --> src/main.rs:816:5
    |
816 |     *ARGS; // make sure `ARGS` is initialized after this point
    |     ^^^^^ move occurs because value has type `ArgMatches<'_>`, which does not implement the `Copy` trait

For a more explicit/readable alternative there’s the option to use

lazy_static::initialize(&ARGS); // make sure arguments are handled at this point

I forgot about that API.

Thanks! Both alternatives work.

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.