149

Coming from Python, I'm not used to see code lines longer than 80 columns. So when I encounter this:

err := database.QueryRow("select * from users where user_id=?", id).Scan(&ReadUser.ID, &ReadUser.Name, &ReadUser.First, &ReadUser.Last, &ReadUser.Email)

I tried to break it to

err := database.QueryRow("select * from users where user_id=?", id) \
    .Scan(&ReadUser.ID, &ReadUser.Name, &ReadUser.First, &ReadUser.Last, &ReadUser.Email)

But I get

 syntax error: unexpected \

I also tried just breaking the line with hitting enter and put a semicolon at the end:

err := database.QueryRow("select * from users where user_id=?", id) 
.Scan(&ReadUser.ID, &ReadUser.Name, &ReadUser.First, &ReadUser.Last, &ReadUser.Email);

But the I again get:

syntax error: unexpected .

So I'm wondering what's the golangic way to do so?

icza
  • 389,944
  • 63
  • 907
  • 827
Karlom
  • 13,323
  • 27
  • 72
  • 116

7 Answers7

165

First some background. The formal grammar of Go uses semicolons ";" as terminators in many productions, but Go programs may omit most of them (and they should to have a clearer, easily readable source; gofmt also removes unnecessary semicolons).

The specification lists the exact rules. Spec: Semicolons:

The formal grammar uses semicolons ";" as terminators in a number of productions. Go programs may omit most of these semicolons using the following two rules:

  1. When the input is broken into tokens, a semicolon is automatically inserted into the token stream immediately after a line's final token if that token is

  2. To allow complex statements to occupy a single line, a semicolon may be omitted before a closing ")" or "}".

So as you can see if you insert a newline character after the parenthesis ), a semicolon ; will be inserted automatically and so the next line will not be treated as the continuation of the previous line. This is what happened in your case, and so the next line starting with .Scan(&ReadUser.ID,... will give you a compile-time error as this standing by itself (without the previous line) is a compile-time error: syntax error: unexpected .

So you may break your line at any point which does not conflict with the rules listed under point 1. above.

Typically you can break your lines after comma ,, after opening parenthesis e.g. (, [, {, and after a dot . which may be referencing a field or method of some value. You can also break your line after binary operators (those that require 2 operands), e.g.:

i := 1 +
        2
fmt.Println(i) // Prints 3

One thing worth noting here is that if you have a struct or slice or map literal listing the initial values, and you want to break line after listing the last value, you have to put a mandatory comma , even though this is the last value and no more will follow, e.g.:

s := []int {
    1, 2, 3,
    4, 5, 6,  // Note it ends with a comma
}

This is to conform with the semicolon rules, and also so that you can rearrange and add new lines without having to take care of adding / removing the final comma; e.g. you can simply swap the 2 lines without having to remove and to add a new comma:

s := []int {
    4, 5, 6,
    1, 2, 3,
}

The same applies when listing arguments to a function call:

fmt.Println("first",
    "second",
    "third",       // Note it ends with a comma
)
icza
  • 389,944
  • 63
  • 907
  • 827
  • 3
    I keep putting a comma at the end of javascript literals. #hatejs – Paulo Scardine Jan 18 '16 at 16:59
  • 47
    TL;DR: **Typically you can break your lines after comma ,, after opening parenthesis e.g. (, [, {, and after a dot . which may be referencing a field or method of some value.** – Peeter Kokk Jul 25 '16 at 18:15
  • 2
    Having to add an extra comma at the end reminds me of some old IE js bugs, terrible – Christophe Roussy Nov 05 '16 at 11:30
  • 8
    @ChristopheRoussy It's subjective, I for one love that all lines have to end with a comma, and I can rearrange and add new lines with ease without having to take care of adding / removing commas. – icza Nov 05 '16 at 15:03
  • @icza, I also like them to end with a comma, but not having to do it for the last one :) – Christophe Roussy Nov 07 '16 at 12:40
  • @PauloScardine as of ES2017 trailing commas are allowed in all contexts in JavaScript https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Trailing_commas – OregonTrail Feb 20 '19 at 17:13
37

The simplest way is to simply leave the operator (.) on the first line.

\ line continuations are also discouraged in many python style guides, you could wrap the whole expression in parens if you are moving back and forth between go and python as this technique works in both languages.

tobyodavies
  • 27,347
  • 5
  • 42
  • 57
23

As mentioned, this is a matter of style preference. I understand that the creators of Go have suggested a style based on their experience of which I learn from but also keep some of my own style from my experience.

Below is how I would format this:

err := database.
  QueryRow("select * from users where user_id=?", id).
  Scan(
    &ReadUser.ID,
    &ReadUser.Name,
    &ReadUser.First,
    &ReadUser.Last,
    &ReadUser.Email,
  )
Ryota
  • 1,157
  • 2
  • 11
  • 27
halorium
  • 709
  • 1
  • 6
  • 8
20

It's a matter of style, but I like:

err := database.QueryRow(
    "select * from users where user_id=?", id,
).Scan(
    &ReadUser.ID, &ReadUser.Name, &ReadUser.First, &ReadUser.Last, &ReadUser.Email,
)
peterSO
  • 158,998
  • 31
  • 281
  • 276
1

what's the golangic way to do so?

An automated solution. Unfortunately, gofmt doesn't cover this case so you could use https://github.com/segmentio/golines

Install it via

go install github.com/segmentio/golines@latest

Then run

golines -w -m 80 .

-w means make the changes in-place (default prints to stdout)

-m is max column length

Mark
  • 1,337
  • 23
  • 34
  • not directly related to question but helpful if someone wants to auto format long lines in golang to avoid lint errors. – user1583465 Feb 13 '23 at 09:21
0

You can break the line at several places like commas or braces as suggested by other answers. But Go community has this opinion on line length:

There is no fixed line length for Go source code. If a line feels too long, it should be refactored instead of broken.

There are several guidelines there in the styling guide. I am adding some of the notable ones (clipped):

  1. Commentary

Ensure that commentary is readable from source even on narrow screens.
...
When possible, aim for comments that will read well on an 80-column wide terminal, however this is not a hard cut-off; there is no fixed line length limit for comments in Go.

  1. Indentation confusion

Avoid introducing a line break if it would align the rest of the line with an indented code block. If this is unavoidable, leave a space to separate the code in the block from the wrapped line.

 // Bad:
 if longCondition1 && longCondition2 &&
     // Conditions 3 and 4 have the same indentation as the code within the if.
     longCondition3 && longCondition4 {
     log.Info("all conditions met")
 }
  1. Function formatting

The signature of a function or method declaration should remain on a single line to avoid indentation confusion.
Function argument lists can make some of the longest lines in a Go source file. However, they precede a change in indentation, and therefore it is difficult to break the line in a way that does not make subsequent lines look like part of the function body in a confusing way:

 // Bad:
 func (r *SomeType) SomeLongFunctionName(foo1, foo2, foo3 string,
     foo4, foo5, foo6 int) {
     foo7 := bar(foo1)
     // ...
 }

 // Good:
 good := foo.Call(long, CallOptions{
     Names:   list,
     Of:      of,
     The:     parameters,
     Func:    all,
     Args:    on,
     Now:     separate,
     Visible: lines,
 })
 
 // Bad:
 bad := foo.Call(
     long,
     list,
     of,
     parameters,
     all,
     on,
     separate,
     lines,
 )

Lines can often be shortened by factoring out local variables.

 // Good:
 local := helper(some, parameters, here)
 good := foo.Call(list, of, parameters, local)

Similarly, function and method calls should not be separated based solely on line length.

 // Good:
 good := foo.Call(long, list, of, parameters, all, on, one, line)
 
 // Bad:
 bad := foo.Call(long, list, of, parameters,
     with, arbitrary, line, breaks)
  1. Conditionals and loops

An if statement should not be line broken; multi-line if clauses can lead to indentation confusion.

 // Bad:
 // The second if statement is aligned with the code within the if block, causing
 // indentation confusion.
 if db.CurrentStatusIs(db.InTransaction) &&
     db.ValuesEqual(db.TransactionKey(), row.Key()) {
     return db.Errorf(db.TransactionError, "query failed: row (%v): key does not match transaction key", row)
 }

If the short-circuit behavior is not required, the boolean operands can be extracted directly:

 // Good:
 inTransaction := db.CurrentStatusIs(db.InTransaction)
 keysMatch := db.ValuesEqual(db.TransactionKey(), row.Key())
 if inTransaction && keysMatch {
     return db.Error(db.TransactionError, "query failed: row (%v): key does not match transaction key", row)
 }

There may also be other locals that can be extracted, especially if the conditional is already repetitive:

 // Good:
 uid := user.GetUniqueUserID()
 if db.UserIsAdmin(uid) || db.UserHasPermission(uid, perms.ViewServerConfig) || db.UserHasPermission(uid, perms.CreateGroup) {
     // ...
 }
 
 // Bad:
 if db.UserIsAdmin(user.GetUniqueUserID()) || db.UserHasPermission(user.GetUniqueUserID(), perms.ViewServerConfig) || db.UserHasPermission(user.GetUniqueUserID(), perms.CreateGroup) {
     // ...
 }

switch and case statements should also remain on a single line.

 // Good:
 switch good := db.TransactionStatus(); good {
 case db.TransactionStarting, db.TransactionActive, db.TransactionWaiting:
     // ...
 case db.TransactionCommitted, db.NoTransaction:
     // ...
 default:
     // ...
 }
 
 // Bad:
 switch bad := db.TransactionStatus(); bad {
 case db.TransactionStarting,
     db.TransactionActive,
     db.TransactionWaiting:
     // ...
 case db.TransactionCommitted,
     db.NoTransaction:
     // ...
 default:
     // ...
 }

If the line is excessively long, indent all cases and separate them with a blank line to avoid indentation confusion:

 // Good:
 switch db.TransactionStatus() {
 case
     db.TransactionStarting,
     db.TransactionActive,
     db.TransactionWaiting,
     db.TransactionCommitted:
 
     // ...
 case db.NoTransaction:
     // ...
 default:
     // ...
 }
  1. Never brake long URLs into multiple lines.

I have only added some of the few examples there are in the styling guide. Please read the guide to get more information.

subtleseeker
  • 4,415
  • 5
  • 29
  • 41
0

You can do this

database.
QueryRow("select * from users where user_id=?", id).
Scan(&ReadUser.ID, &ReadUser.Name, &ReadUser.First, &ReadUser.Last, &ReadUser.Email)