28
\$\begingroup\$

What general tips do you have for golfing in Go? I'm new to Code Golfing and looking for ideas that can be applied to code golf problems in general that are at least somewhat specific to Go.

Please keep to one tip per answer.

\$\endgroup\$
3
  • 5
    \$\begingroup\$ We might want to create the Go tag, but then, is Go even being used for golfing here ? \$\endgroup\$
    – Optimizer
    Commented Nov 25, 2014 at 9:51
  • \$\begingroup\$ @Optimizer I used to \$\endgroup\$
    – cat
    Commented Mar 16, 2016 at 1:58
  • 7
    \$\begingroup\$ Should rename the title to "Tips for Go-lfing" \$\endgroup\$
    – Uriel
    Commented Sep 5, 2017 at 18:02

19 Answers 19

19
\$\begingroup\$

Inspired by @EMBLEM's answer here.

You can put a package's functions in the global namespace when you import them like so:

package main

import ."fmt"

func main() {
    Printf("Hello World!")
}
\$\endgroup\$
12
\$\begingroup\$

Named return values can save a few bytes. For example:

func x()string{
r:="" //Do stuff
return r}

You can save 3 bytes with

func x()(r string){
//Do stuff
return}

It's more useful if you need to declare multiple variables at the start of your function.

\$\endgroup\$
12
\$\begingroup\$

You can name packages whatever you like when you import them.

package main

import f "fmt"

func main() {
    f.Printf("Hello World\n")
}

Learned this here.

\$\endgroup\$
2
  • 4
    \$\begingroup\$ You can also do import ."fmt" and then Println and the rest of fmt's functions are in the global namespace. Also, you don't need semicolons. Ever, unless you have multiple statements on a line \$\endgroup\$
    – cat
    Commented Mar 16, 2016 at 1:59
  • 1
    \$\begingroup\$ Note that in Golang, braces must go on the same line, because of automatic semicolon insertion. More info: golang.org/doc/faq#semicolons. \$\endgroup\$
    – cat
    Commented Mar 22, 2016 at 22:41
8
\$\begingroup\$

If you need to compare many different values to a single one, it may be more space-efficient to use a switch with a single case.

if x==1||x==2||x==3||x==4{}
switch x{case 1,2,3,4:}
\$\endgroup\$
5
\$\begingroup\$

Go compiler has predefined print and println functions that don't require importing fmt, so instead of this.

package main
import."fmt"
func main(){Printf(`Hello World
`)}

You can write this.

package main
func main(){print(`Hello World
`)}

Note that this outputs to STDERR.

\$\endgroup\$
4
\$\begingroup\$

Go has different operator precedence for bit operations, <<, >>, &, etc. usually have lower precedence than + and - in most languages, but in Go they have the same precedence as * and /.

Precedence    Operator
5             *  /  %  <<  >>  &  &^
4             +  -  |  ^
3             ==  !=  <  <=  >  >=
2             &&
1             ||

This could be used to save some parentheses.

Most languages:

(a&b)*c

Go:

a&b*c
\$\endgroup\$
4
\$\begingroup\$

A lot of stuff in the for range loop is optional.

Standard version:

for i,v:=range a{
// Do stuff
}

If i, v has already been defined and can be overwritten:

for i,v=range a{
// Do stuff
}

If you don't care about value:

for i:=range a{
// Do stuff
}

If you don't care about value and i has already been defined:

for i=range a{
// Do stuff
}

If you don't care about index or value:

for range a{
// Do stuff
}

If you want an infinite loop:

for{
// Do stuff
}
\$\endgroup\$
4
\$\begingroup\$

Need a string to contain a newline? Don't write \n, create a raw string with backquotes and put a literal newline in it.

s:="\n" // 7 bytes
s:=`
` // 6 bytes
\$\endgroup\$
2
\$\begingroup\$

Declaring Multiple Variables:

i,s:=0,""

var(i int;s string)

Int From String Conversion: (limited but sometimes helpful)

n:=byte("9"[0])-48 // actual type is uint8

n,_:=strconv.Atoi("9")

And Vice Versa

s:=string(9+48)

s:=strconv.Itoa(9)
\$\endgroup\$
3
  • \$\begingroup\$ fmt.Sprint is likely to save bytes over strconv.Atoi, because you've likely imported fmt already. \$\endgroup\$
    – EMBLEM
    Commented Sep 29, 2016 at 18:54
  • \$\begingroup\$ @EMBLEM I think strconv was there just to show what the golf-code would be in normal code. \$\endgroup\$ Commented Aug 5, 2019 at 14:01
  • \$\begingroup\$ Assuming fmt is already imported, n:=0;Sscan("9",&n) also works and is not limited to one digit numbers. \$\endgroup\$
    – primo
    Commented Apr 22, 2020 at 10:19
2
\$\begingroup\$

When looping a constant n times, for-range is shorter up to n <= 5

If you need to loop a constant number of times, a for range"..."{} loop (where ... is n characters) is shorter than a 3-part for loop up to n <= 5 iterations.

The two loop forms are equivalent at n == 5:

for range`|||||`{}
for i:=0;i<5;i++{}

Past n > 5, use a 3-part loop.

\$\endgroup\$
1
\$\begingroup\$

You can put any number of opening braces on one line, but a line that contains opening braces may contain at most one closing brace.

Correct:

func main(){if true{switch{case 1==1:for{break
}}}}

Also correct:

func main(){if true{switch{case 1==1:for{break}
}}}

Also correct:

func main(){if true{switch{case 1==1:for{
break}}}}

Incorrect:

func main() {
    if true{for{break}}
}
\$\endgroup\$
1
\$\begingroup\$

Since an allowable output format is writing to reference arguments, it may save bytes to pass a pointer instead of returning a value.

func f(a,b string)string{if g(a)>g(b){return a};return b}
func f(a,b string, s*string){s=&b;if g(a)>g(b){s=&a}}
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Or func f(a,b string)string{if g(a)>g(b){b=a};return b} \$\endgroup\$
    – JayXon
    Commented Jun 24, 2020 at 4:43
1
\$\begingroup\$

If you're using a C-style 3-part for-loop, if your loop variable isn't dependent on something outside of its scope for its initialization, declare it with your other variable declarations to save a byte.

In these examples, i is initialized independently of a

func f(){a:=3;for i:=0;i<a;i++{}}
// vs
func f(){a,i:=3,0;for;i<a;i++{}}
\$\endgroup\$
1
\$\begingroup\$

When taking a single character substring from a string and want to keep it as a string, use string slices to not get a rune:

string(s[0]) // 12 bytes
s[:1]        // 4 bytes

string(s[10]) // 13 bytes
s[10:11]      // 8 bytes

string(s[100]) // 14 bytes
s[100:101]     // 10 bytes

string(s[1000]) // 15 bytes
s[1000:1001]    // 12 bytes

string(s[10000]) // 16 bytes
s[10000:10001]   // 14 bytes

This method is shorter than string(s[index]) up to getting from index 100000 and above, but indexing strings of that size are rare.

\$\endgroup\$
1
  • 4
    \$\begingroup\$ I suppose s[index:][:1] would work too. \$\endgroup\$ Commented Dec 17, 2022 at 6:07
1
\$\begingroup\$

Don't declare variables in the for's init statement:

for i:=0;i<n;i++{...}

By declaring it outside you may save a byte when you need a variable outside of it beacause you can now assign to it (=) instead of declaring it (:=):

i:=0
for;i<n;i++{...}
i=0
for;i<n;i++{...}

You might even save a byte if you're not using the for's post-statement by removing the semicolons

\$\endgroup\$
0
\$\begingroup\$

Make full use of Go's first-class functions by assigning long library function names to one-letter variables.

import."strings"
r:=Replace
\$\endgroup\$
0
\$\begingroup\$

Shuffle elements with rand.Perm

I discovered math/rand.Perm the other day, which returns a slice of integers from 0 to n, shuffled.

For strings

Given an input string k, and you need to return a string, and we have that string as a named return, Perm is shorter because you don't need to real with the boilerplate for Shuffle or the swapping:

Shuffle(len(k),func(i,j int){k[i],k[j]=k[j],k[i]}) // 48 bytes
for i:=range k{j:=Intn(i);k[i],k[j]=k[j],k[i]}     // 44 bytes, -4, no separate string needed
for i:=range Perm(len(k)){e+=k[i:i+1]}             // 36 bytes, -12

For slices

Assuming the same conditions as above but with a slice, it is still shorter:

Shuffle(len(k),func(i,j int){k[i],k[j]=k[j],k[i]}) // 48 bytes
for i:=range k{j:=Intn(i);k[i],k[j]=k[j],k[i]}     // 44 bytes, -4, no separate slice needed
for i:=range Perm(len(k)){e=append(e,k[i])}        // 41 bytes, -7
\$\endgroup\$
0
\$\begingroup\$

Use min and max to clamp values (Go 1.21+)

Say you have 2 comparables l and h, where you must satisfy l <= h. It is 1 byte shorter to use the builtins than an if-statement.

// assuming
l,h := n, 0

if h<l{h=l}
h=max(h,l)

if l>h{l=h}
l=min(h,l)

// and the opposite
l,h := 0, n

if h>l{h=l}
h=min(h,l)

if l<h{l=h}
l=max(h,l)
\$\endgroup\$
0
\$\begingroup\$

Use range over int when iterating n times (Go 1.22 or newer)

Introduced in Go 1.22 is range-over-int, which iterates from 0 inclusive to the integer exclusive:

for i := range 123 {} // goes from 0 to 123 exclusive

It is also conveniently always shorter than the traditional 3-part for loop:

for i:=0;i<n;i++{}
for i:=range n{}   // -2 bytes

However, this requires Go 1.22 or later.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.