Join and map command line arguments in Rust
I've collected a few thoughts about possible refactors you could consider below.

Your if chain isn't the most efficient way

As you've written it, your code will only work for titles between one and four words long. This isn't a disaster, and it will probably work for most titles, but we can rewrite your code to be shorter and handle any length of title. A win-win!


We can use Iterator::skip() to throw away the executable name, as we don't care about it anyway. See skip() in the documentation.

let args: Vec&lt;String&gt; = env::args().skip(1).collect();

It'd be a good idea to check that we've been given enough arguments before continuing.

let arg_len: usize = args.len();
if arg_len &lt; 2 {
    panic!("At least two arguments must be supplied!");
}

Now, we can be a bit clever and avoid your if chain all together. Using a range we can get a slice of our args vector corresponding to all but the last element. We can then use join() to turn that slice back into a String. For example, if we started with vec!["Song", "of", "Solomon", "2:2-4"] in args, we would take a slice to get ["Song", "of", "Solomon"] and rejoin them with " " in the middle to get "Song of Solomon".

let book = args[0..(arg_len - 1)].join(" ");



Try to avoid clone() unless necessary

The first thing I noticed when I took a look at your code was that you've used clone() in quite a few places. We've already got rid of a lot of them with the tweak above, and you should try to avoid them all together as allocating when you don't need to is a waste of time and memory. If you can use an &amp;str, do that instead of insisting on all strings being String. As an aside, there is rarely any point bothering with &amp;String — just use &amp;str there.


Putting the above into action, we can let reference be an &amp;str and avoid a clone.

let reference: &amp;str = &amp;args[args.len() - 1];

Let's also change get_book's method signature as suggested above.

fn get_file(book: &amp;str) -&gt; &amp;str {

Now we can get rid of the unnecessary to_string() below.

books_to_files.get::&lt;str&gt;(&amp;book.to_string()).unwrap()

And tweak this line in main() to accept an &amp;str.

let file: &amp;str = get_file(&amp;book);



Avoiding unwrap

Ideally, get_book ought to return an Option instead, so the caller can choose how they want to handle the error. As it is fatal anyway, unwrapping makes little difference, but it's worth bearing in mind as a future improvement.

Try it Online