Now that you’ve had a glimpse of Go and completed your first exercise, we’re going to dive deep. Our first stop on our journey is variables.
A variable holds data for you temporarily so that you can work with it. When you declare a variable, it needs four things: a statement that you are declaring a variable, a name for the variable, the type of data it can hold, and an initial value for it. Fortunately, some of the parts are optional, but that also means there’s more than one way of defining a variable.
Let’s cover all the ways you can declare a variable.
Declaring a variable using var
Using var
is the foundational way to declare a variable. Every other way we’ll cover is a variation of this approach, typically by omitting parts of this definition. A full var
definition with everything in place looks like this:
var foo string = "bar"
The key parts are var
, foo
, string
, and = "
bar"
:
var
is our declaration that we are defining a variable
foo
is the name of the variable
string
is the type of the variable
= "bar"
is its initial value
Exercise 1.02 – declaring a variable using var
In this exercise, we’ll declare two variables using the full var
notation. Then, we’ll print them to the console. You’ll see that you can use the var
notation anywhere in your code, which isn’t true for all variable declaration notations. Let’s get started:
- Create a new folder and add a
main.go
file to it.
- In
main.go
, add the main package name to the top of the file:package main
- Add the imports:
import (
"fmt"
)
- Declare a variable at the package-level scope. We’ll cover what scopes are in detail later:
var foo string = "bar"
- Create the
main()
function:func main() {
- Declare another variable using
var
in our function: var baz string = "qux"
- Print both variables to the console:
fmt.Println(foo, baz)
- Close the
main()
function:}
- Save the file. Then, in the new folder, run the following:
go run .
The following is the output:
bar qux
In this example, foo
is declared at the package level while baz
is declared at the function level. Where a variable is declared is important because where you declare a variable also limits what notation you can use to declare it.
Next, we’ll look at another way to use the var
notation.
Declaring multiple variables at once with var
We can use a single var
declaration to define more than one variable using a var
block or statement. Using this method is common when declaring package-level variables. The variables don’t need to be of the same type, and they can all have their own initial values. The notation looks like this:
var (
<name1> <type1> = <value1>
<name2> <type2> = <value2>
…
<nameN> <typeN> = <valueN>
)
You can have multiple of these types of declarations. This is a nice way to group related variables, thereby making your code more readable. You can use this notation in functions, but it’s rare to see it used there.
Exercise 1.03 – declaring multiple variables at once with var
In this exercise, we’ll declare multiple variables using one var
statement, each with a different type and initial value. Then, we’ll print the value of each variable to the console. Let’s get started:
- Create a new folder and add a
main.go
file to it.
- In
main.go
, add the main
package name to the top of the file:package main
- Add the imports:
import (
"fmt"
"time"
)
- Start the
var
declaration:var (
- Define three variables:
Debug bool = false
LogLevel string = "info"
startUpTime time.Time = time.Now()
- Close the
var
declaration:)
- In the
main()
function, print each variable to the console:func main() {
fmt.Println(Debug, LogLevel, startUpTime)
}
- Save the file. Then, in the new folder, run the following:
go run .
The following is the output:
Figure 1.5: Output displaying three variable values
In this exercise, we declared three variables using a single var
statement. Your output will look different for the time.Time
variable, but that’s correct. The format is the same, but the time itself is different.
Using the var
notation like this is a good way to keep your code well organized and save you some typing.
Next, we’ll start removing some of the optional parts of the var
notation.
Skipping the type or value when declaring variables
In real-world code, it’s not common to use the full var
notation. There are a few cases where you need to define a package-level variable with an initial value and tightly control its type. In those cases, you need the full notation. It’ll be obvious when this is needed as you’ll have a type mismatch of some kind, so don’t worry too much about this for now. The rest of the time, you’ll remove an optional part or use the short variable declaration.
You don’t need to include both the type and the initial value when declaring a variable. You can use just one or the other; Go works out the rest. If you have a type in the declaration but no initial value, Go uses the zero value for the type you picked. We’ll talk about what a zero value is later in this book. On the other hand, if you have an initial value and no type, Go has a ruleset for how to infer the types that are needed from the literal value you use.
Exercise 1.04 – skipping the type or value when declaring variables
In this exercise, we’ll update our previous exercise so that it skips the optional initial values or type declarations from our variable declaration. Then, we’ll print the values to the console, as we did previously, to show that the result is the same. Let’s get started:
- Create a new folder and add a
main.go
file to it.
- In
main.go
, add the main
package name to the top of the file:package main
- Import the packages we’ll need:
import (
"fmt"
"time"
)
- Start the multi-variable declaration:
var (
- The
bool
value in the first exercise has an initial value of false. That’s a bool
value’s zero value, so we’ll drop the initial value from its declaration as it is set by default: Debug bool
- The next two variables both have a non-zero value for their type, so we’ll drop their type declaration:
LogLevel = "info"
startUpTime = time.Now()
- Close the
var
declaration:)
- In the
main()
function, print out each variable:func main() {
fmt.Println(Debug, LogLevel, startUpTime)
}
- Save the file. Then, in the new folder, run the following:
go run .
The following is the output:
Figure 1.6: Output displaying variable values despite not mentioning the type while declaring the variables
In this exercise, we were able to update the previous code so that it uses a much more compact variable declaration. Declaring variables is something you’ll have to do a lot, and not having to use the notation makes for a better experience when writing code.
Next, we’ll look at a situation where you can’t skip any of the parts.
Type inference gone wrong
There are times when you’ll need to use all the parts of the declaration – for example, when Go isn’t able to guess the correct type you need. Let’s take a look at an example of this:
package main
import "math/rand"
func main() {
var seed = 1234456789
rand.NewSource(seed)
}
The following is the output:
Figure 1.7: Output showing an error
The issue here is that rand.NewSource
requires a variable of the int64
type. Go’s type inference rules interoperate a whole number, such as the one we used as an int
value. We’ll look at the difference between them in more detail later in this book. To resolve this, we will add int64
type to the declaration. Here’s how that looks:
package main
import "math/rand"
func main() {
var seed int64 = 1234456789
rand.NewSource(seed)
}
Next, we’ll look at an even quicker way to declare variables.
Short variable declaration
When declaring variables in functions and functions only, we can use the :=
shorthand. This shorthand allows us to make our declarations even shorter. It does this by allowing us to not have to use the var
keyword and by always inferring the type from a required initial value.
Exercise 1.05 – implementing a short variable declaration
In this exercise, we’ll update our previous exercise so that it uses a short variable declaration. Since you can only use a short variable declaration in a function, we’ll move our variable out of the package scope. Where before Debug
had a type but no initial value, we’ll switch it back so that it has an initial value since that’s required when using a short variable declaration. Finally, we’ll print it to the console. Let’s get started:
- Create a new folder and add a
main.go
file to it.
- In
main.go
, add the main
package name to the top of the file:package main
- Import the packages we’ll need:
import (
"fmt"
"time"
)
- Create the
main()
function:func main() {
- Declare each variable using the short variable declaration notation:
Debug := false
LogLevel := "info"
startUpTime := time.Now()
- Print the variables to the console:
fmt.Println(Debug, LogLevel, startUpTime)
}
- Save the file. Then, in the new folder, run the following:
go run .
The following is the output:
Figure 1.8: Output displaying the variable values that were printed after using short variable declaration notation
In this exercise, we updated our previous code to use a very compact way to declare variables when we have an initial value to use.
The :=
shorthand is very popular with Go developers and the most common way in which variables get defined in real-world Go code. Developers like how it makes their code concise and compact while still being clear as to what’s happening.
Another shortcut is declaring multiple variables on the same line.
Declaring multiple variables with a short variable declaration
It’s possible to declare multiple variables at the same time using a short variable declaration. They must all be on the same line, and each variable must have a corresponding initial value. The notation looks like <var1>, <var2>, …, <varN> := <val1>, <val2>, …, <valN>
. The variable names are on the left-hand side of:=
, separated by ,
. The initial values are on the right-hand side of:=
again, each separated by ,
. The leftmost variable name gets the leftmost value. There must be an equal number of names and values.
Here is an example that uses our previous exercise’s code:
package main
import (
"fmt"
"time"
)
func main() {
Debug, LogLevel, startUpTime := false, "info", time.Now()
fmt.Println(Debug, LogLevel, startUpTime)
}
The following is the output:
Figure 1.9: Example output displaying the variable values for the program with a variable declaring function
Sometimes, you do see real-world code like this. It’s a little hard to read, so it’s not common to see it in terms of literal values. This doesn’t mean this isn’t common, though – it’s very common when calling functions that return multiple values. We’ll cover this in detail when we look at functions later in this book.
Exercise 1.06 – declaring multiple variables from a function
In this exercise, we’ll call a function that returns multiple values, and we’ll assign each value to a new variable. Then, we’ll print the values to the console. Let’s get started:
- Create a new folder and add a
main.go
file to it.
- In
main.go
, add the main
package name to the top of the file:package main
- Import the packages we’ll need:
import (
"fmt"
"time"
)
- Create a function that returns three values:
func getConfig() (bool, string, time.Time) {
- In the function, return three literal values, each separated by
,
: return false, "info", time.Now()
- Close the function:
}
- Create the
main()
function:func main() {
- Using a short variable declaration, capture the values that were returned from the function’s three new variables:
Debug, LogLevel, startUpTime := getConfig()
- Print the three variables to the console:
fmt.Println(Debug, LogLevel, startUpTime)
- Close the
main()
function:}
- Save the file. Then, in the new folder, run the following:
go run .
The following is the output:
Figure 1.10: Output displaying the variable values for the program with the variable declaring function
In this exercise, we were able to call a function that returned multiple values and capture them using a short variable declaration in one line. If we used the var
notation, it would look like this:
var (
Debug bool
LogLevel string
startUpTime time.Time
)
Debug, LogLevel, startUpTime = getConfig()
Short variable notation is a big part of how Go has the feel of a dynamic language.
We’re not quite done with var
yet, though – it still has a useful trick up its sleeve.
Using var to declare multiple variables in one line
While it’s more common to use a short variable declaration, you can use var
to define multiple variables on a single line. One limitation of this is that, when declaring the type, all the values must have the same type. If you use an initial value, then each value infers its type from the literal value so that they can differ. Here’s an example:
package main
import (
"fmt"
"time"
)
func getConfig() (bool, string, time.Time) {
return false, "info", time.Now()
}
func main() {
// Type only
var start, middle, end float32
fmt.Println(start, middle, end)
// Initial value mixed type
var name, left, right, top, bottom = "one", 1, 1.5, 2, 2.5
fmt.Println(name, left, right, top, bottom)
// works with functions also
var Debug, LogLevel, startUpTime = getConfig()
fmt.Println(Debug, LogLevel, startUpTime)
}
The following is the output:
Figure 1.11: Output displaying variable values
Most of these are more compact when using a short variable declaration. This fact means they don’t come up in real-world code much. The exception is the same type-only example. This notation can be useful when you need many variables of the same type, and you need to control that type carefully.
Non-English variable names
Go is a UTF-8 compliant language, which means you can define variables’ names using alphabets other than the Latin alphabet that, for example, English uses. There are some limitations regarding what the name of a variable can be. The first character of the name must be a letter or _
. The rest can be a mixture of letters, numbers, and _
. Let’s have a look at what this looks like:
package main
import (
"fmt"
"time"
)
func main() {
デバッグ := false
日志级别 := "info"
ይጀምሩ := time.Now()
_A1_Μείγμα := ""
"
fmt.Println(デバッグ, 日志级别, ይጀምሩ, _A1_Μείγμα)
}
The following is the output:
Figure 1.12: Output showing variable values
Note
Languages and language: Not all programming languages allow you to use UTF-8 characters as variables and function names. This feature could be one of the reasons why Go has become so popular in Asian countries, particularly in China.