I'm learning Go as my first programming language, and I keep running into the choice between concise one-liners and more explicit step-by-step code. Tutorials and coding exercises often use compact helper functions or put an entire expression directly in a return statement, but I usually find the expanded version easier to understand.
For example, to select a random element from a slice, I might write:
func pickRandomFrom(items []string) string {
length := len(items)
index := rand.Intn(length)
return items[index]
}
Alternatively, I could write the operation directly as items[rand.Intn(len(items))], without a helper function or intermediate variables.
The longer version seems more readable to me because I don't have to mentally unpack nested parentheses and operator order. A clearly named function also makes the calling code read more like a sentence. However, I worry that the intermediate variables are unnecessary and that experienced Go developers may consider the shorter version more idiomatic.
Is there a useful rule of thumb, style guide, or book chapter for deciding when to keep an expression inline and when to split it into several statements?
4 Answers
There isn't a strict rule. One-liners are perfectly fine when they do one obvious thing, but there is nothing wrong with writing the steps separately either. A couple of extra lines won't hurt anyone, and readability should matter more than minimizing line count.
The best test is how easily someone can understand the code later, perhaps months after writing it. Sometimes five clear lines are better than one dense line, while other times breaking a simple operation apart just adds noise. Aim for balance.
Multiple statements can also make debugging easier. You can inspect intermediate values, set a breakpoint on a particular step, or add temporary logging without having to untangle a nested expression. This matters more when the calls involved aren't already well-tested library functions.
For your example, I might use a named randomIndex and then return items[randomIndex]. The compiler can optimize simple temporary variables, so choosing the clearer form generally isn't a performance concern.
Good point about debugging. Being able to inspect the intermediate result is a practical reason to avoid cramming everything into one expression.
A temporary variable is worthwhile when it gives a meaningful concept a name. For example, randomIndex := rand.Intn(len(items)) is useful because it separates the idea of choosing an index from the act of indexing the slice. On the other hand, length := len(items) doesn't add much because len(items) is already self-explanatory.
When you're unsure, split the expression up, but don't automatically create a variable for every tiny operation. Too many steps can obscure the algorithm just as much as an overly dense one-liner.
That distinction helps. I was mainly using the length variable to demonstrate the expanded style, but I agree that it doesn't improve this particular example.
Exercises and coding challenges often encourage compact solutions because the code is short-lived and people are showing off language features. Production code has different priorities: it needs to be understandable, maintainable, and easy for other people to modify.
A helper such as pickRandomFrom(items) is reasonable if that operation appears repeatedly or if the name communicates an important intention. If it is used only once and the expression is already obvious, keeping it inline may be simpler. In either case, clear names and straightforward logic matter more than following a universal one-line rule.

That makes sense. The useful question is probably not whether the code can be shorter, but whether the shorter version still communicates its intent immediately.