I just completed the bottles of beer exercise on Exercism.io. We really have it good with Strings in Java and C# let me tell you. Having to go back and forth between String and &str took me a little while to understand why that was even required. I think this is a place where the Rust book and Rust by Example could use some beefing up.

The same relationship also exists between Vec<T> and &[T], though it perhaps doesn’t hurt as much since it’s not too common that one wants to do something to a list that might change its length (whereas this is frequent with strings).

By the way, if you post your code, maybe we can point out some tips. (also, it’s good for the community and those who work on documentation to see what kind of code is written by those exploring the language)

I think in Rust it’s more idiomatic to create a formatter instead of manually creating a string. This way you’re not allocating a String unless needed. For example, if you’re printing out your verse immediately, there’s no need to store the string in memory.

To convert str to String, you can use String::from(s) or s.to_string().

s.to_owned() is another, archaic way to do this and the only reason people used to use it was that s.to_string() used to be slow.

If the string is a literal, format!("the string") is another option. I’m not sure if this has a performance penalty associated with it, but I often like to use it for consistency with nearby code.

string += str; is another way to write string.push_str(str);

It is idiomatic to write &string instead of string.as_str() when possible[1]. Basically, this works because &String is allowed to coerce into &str. (for similar reasons, all methods defined on &str and &mut str are available on String).

It is idiomatic to not write return on the final statement of a function; just write the thing you are returning, without a semicolon.

Personally, I seldom use addition on strings since format! is quite versatile (as long as you aren’t repeatedly using format!() to build onto the same string, which could turn O(n) work into O(n^2)). Here’s how I might have written it:

verse.push_str(bottles_string(n).as_str());
verse.push_str(" of beer on the wall, ");
verse.push_str(bottles_string(n).as_str());
verse.push_str(" of beer.\nTake one down and pass it around, ");
verse.push_str(bottles_string(n - 1).as_str());
verse.push_str(" of beer on the wall.\n");

While there’s far more to it than just this, there’s a decent parallel between “If I’m making a new one, I’ll use String (Rust) or StringBuilder (C#), but if I’m just accepting an existing one I’ll use str (Rust) or string (C#)”. (Lifetimes being the huge difference that statement ignores.)

pub fn sing(start: i32, end: i32) -> String {
// a more iteratory way to handle the final newline.
// ('join' is a method on `&[String]` as well as `&[&str]`,
// and all &[T] methods are available on `Vec<T>`.)
let verses: Vec<T> = (start..end + 1).rev().map(verse).collect();
verses.join("\n")
}

I’d be willing to bet the itertools solution is ultimately sloppier with allocations. After all, I can only assume that the reason that the stdlib join takes a slice in the first place is so that it can precompute the total length of the string.

(that said, I would kill to have Itertools::join in the standard library, just because it’s so convenient!)

Update:wtf

The stdlib does precompute the total length of the string… incorrectly.

That’s the great thing about these exercism assignments. They are SO trivial that you have mental capacity to spare to really reflect on your bad habits and style choices. This “going back to basics” has really helped me confront myself.

StefanoChiodino:

The brevity of steveklabnik made me seriously reflect.

Exactly! These small exercises really allow you to play with the different trade-offs. In this case, readability Vs brevity Vs Don’t-Repeat-Yourself.
Arguably, DRY usually improves readability, but as Steve Vs ours shows, not always

I believe the appropriate term here is “gold plating”: spending lots of time on fancy tricks that add zero functionality