Static Initializer Block On Types?

Am I correct in thinking that Rust does not support static initializer blocks on types the way that other languages do such as Java with it's static {} or Objective-C with it's + (void) initialize {} that are only run once on first access to any feature provided by that type?

Largely, yes. What is it you want to achieve?
Depending on that, you might be able to use the lazy_static crate.

1 Like

The idea is to build up a tree structure where each node adds itself to the tree before the tree is ever called upon to identify a target node to execute (each node is a unit of work).

I don't know much about lazy_static. Is it solely for assigning values to static variables or can you define blocks of code to run (that for example, simply call a number of associated functions)?

Also, when does the lazy_static code get called? For example, in what order, if I have defined one in each module?

It’s mostly a way to init statics at runtime where some non const fn’able facilities are needed (eg dynamic memory allocation). The init is done on first access to the static.

Why do you want to build a tree via statics?

I'm thinking of developing a CLI tool that generates stub code for CLI commands and subcommands. It's a bit meta. Each .rs file with the command code within, will add itself to a static tree so that it's available before main is executed.

The idea is to reduce the amount of infrastructure code developers (read: myself) write when developing a CLI app. All they really have to do is provide a function and some metadata about the command, and call a single entry point in main (such as commander::run()) and the CLI args passed to the executing binary are used to navigate the tree to identify which command to run.

It does look like I'll have to find another way to do this in Rust though.

1 Like

Have you seen the existing clap and structopt crates?

1 Like

Have you thought about essentially just writing out the core and 1 or 2 commands, and then abstracting over what turns out to be boilerplate? In general I find this a powerful technique to solve problems that involve inventing new abstractions, as is the case here.

Personally I only go looking to things like statics for performance reasons, i.e. usually after I have a solid design. For example, I almost exclusively use it to store compiled Regexes, as re-compilation is pretty expensive. So I don't think that statics are really all that necessary here, as the code necessary to set up a CLI application is not at all demanding, even of hardware like a raspberry pi.

I do have one question: Why a tree-like design? What does each node represent?

I've seen clap but I don't like the builder pattern, and I can't see where it actually incorporates the code that should run for each command. It seems focused solely on argument parsing, and not code execution beyond that. I could be wrong though.

Yeah, I don't think it handles execution. Parsing is typically the laborious part, and so it handles that. However, you can consider a similar approach to structopt (i.e. a custom derive) that bolts on execution.

I want to cater to CLI apps that have commands and subcommands, and ... , so a tree structure is a good way to represent this. Each node represents a command with it's usage, help text, arguments, including the function that will be invoked once I find the correct node in the tree that matches the arguments passed to the executable.

1 Like

Does custom derive and attributes involve procedural macros? I was very intrigued by how the Rocket web framework uses procedural macros to generate code (not that I've had a close look at it yet - don't recall seeing much on procedural macros online tbh), but I'm a bit concerned it's too advanced for me at this stage in my Rust learning experience.

Yeah, it's proc(edural) macros. It is somewhat advanced (I don't have much experience with them myself, but have looked at some code here and there), but would be a good opportunity to learn a very cool and useful trick in the Rust toolbox :slight_smile:. You can probably get quite a bit of insight/inspiration from structopt, as mentioned, since it essentially does all that you want except doesn't provide a way to wire up a callback/action for an option.

Thanks for mentioning this. After seeing Rocket I was quite keen to look into it further, because it would be quite useful to annotate associated functions (a built target - I'm talking about build tools now) with a CLI name and dependency list, to make it easy to write code around cargo, much like Ant and Gradle.

There's a bit of a procedural macro walk-through in the first edition, if you're curious.

This crate was mentioned today in the Crate of the Week thread and looks like what you want to do (ie bind CLI commands to rust functions).

1 Like

Thanks for showing me this. It looks like I can learn from it. :slight_smile: