How to process file uploads in Go
This article will teach you how you can upload single or multiple files to a Golang web server, and show progress reporting on file uploads
Can you program without variables? Maybe in some esoteric languages, but in Go, you can’t. So it’s important to get acquainted with how variables work in the language so that you can write effective programs. And that’s what this article is all about.
Variables are an essential component of Go programs. A variable is a name given to a location in memory where some data is stored. Each variable also has a data type (string
, int
, bool
e.t.c.) associated with it.
To write good Go programs, you need to learn the idiomatic way of working with variables in the language, so that’s what this tutorial will cover. You can run the code snippets presented on this page in the Go playground, or locally on your computer if you prefer.
Before we discuss how variables are created in Go, let’s look at the naming conventions that you should be aware of. A name in Go begins with a Unicode letter or an underscore, followed by any number of additional letters, digits and underscores. Names are also case sensitive in Go, so fetchNews
and fetchnews
are different names.
The convention in Go is to use camelCase for names, and Go programmers tend to lean towards short names especially if the scope of the variable is small, although there is no limit for how long a name could be. Go also has 25 keywords such as var
and func
which cannot be used as identifiers in the language.
The snippet below shows the two main ways of creating variables in Go:
package main
import (
"fmt"
)
func main() {
var hello = "Hello"
world := "world!"
fmt.Println(hello, world) // Hello world!
}
We have a variable hello
that is declared using the var
keyword and it is assigned the string “Hello” and a second variable world
declared using the short variable declaration syntax and is assigned the string “world!”. The fmt.Println()
method subsequently prints the text “Hello world!” to the standard output.
There are some differences between these two ways of creating variables in Go. Let’s talk about the var
keyword first.
A var
declaration has the general form shown below:
var name [type] = expression
such as
var str string = "A String"
Either the type or the expression may be omitted, but not both at the same time. As you’ve already seen, the hello
variable declared in the previous section omits the type but includes the expression. In the case where the type is omitted from the declaration, it would be inferred from the expression. This is known as type inference and it makes it easy to declare a variable without having to explicitly annotate its type.
Here are a some examples:
var a = "Hello"
var b = 23
var c = true
var d = 2.3
fmt.Printf("The type of a, b, c, d is: %T, %T, %T and %T respectively", a, b, c, d)
// Prints: The type of a, b, c, d is: string, int, bool and float64 respectively
It’s also possible to declare multiple variables using a single var
declaration as shown below:
var a, b, c, d = "Hello", 23, true, 2.3
// or
var (
a = "Hello"
b = 23
c = true
d = 2.3
)
Go allows you to create variables without explicitly initialising them to a value. In this case, the type of the variable must be present.
var a string
var b int
var c bool
var d float64
// which can also be written as
var (
a string
b int
c bool
d float64
)
If you’re declaring multiple variables that are all of the same type without initialising them, you can use the syntax below:
var a, b, c int // a, b, c are all of type int
When the expression part of a variable declaration is omitted, the variable will be assigned the zero value for that type which is 0
for number types, an empty string ("") for strings, false
for booleans, and nil
for interfaces and pointers.
Here’s an example that demonstrates this concept:
package main
import (
"fmt"
)
func main() {
var (
a string
b int
c bool
d float64
)
fmt.Println(a, b, c, d) // Output: "" 0 false 0
}
This concept of zero values has an important implication that you must be aware of: there is nothing like an uninitialised variable in Go. A variable will always contain a value, either the one you assigned to it, or an implicitly assigned zero value for its type. This behaviour exempts Go programmers from dealing with the uninitialised variable problem that exists in other programming languages.
The second type of variable declaration in Go is called the short variable declaration using the :=
operator. This is the way the majority of variables are declared in Go due to its terse syntax.
str := "A string"
ans := 22 + 20
Just like var
declarations, multiple short variable declarations can be made in a single line:
a, b, c := 1, 2, 3
d, e, f := "Hello", true, 5.8
Unlike var
declarations, there is no implicit assignment to zero values when using the short variable declaration syntax, and you cannot annotate the type of the variable; it is always inferred from the expression on the right-hand side.
You can assign to an already declared variable using the =
operator. All variables are mutable in Go, but the type associated with a variable cannot be changed after declaration.
var name = "sally"
fmt.Println(name) // sally
name = "polly"
fmt.Println(name) // polly
name = true // cannot use true (type untyped bool) as type string in assignment
You can also assign to several variables at once using another form called tuple assignment. The right-hand side expression is evaluated before any left-hand side variables are updated.
// declaration
a, b := 1, 2
// tuple assignment
a, b = a + 1, b + 2 // 2, 4
When using this tuple assignment style to assign variables to a function’s return values, the number of variables must match the number of return values.
A variable in Go can be declared either at package or block level. A package-level variable is one that appears outside of any function. It is accessible throughout the package and can only be a var
declaration due to the :=
operator not being available outside functions.
On the other hand, a block-level variable is one that is declared inside a block such as inside a function, for
loop, if
block, or even a standalone block. The idiomatic way to declare block-level variables is by using the short variable declaration syntax.
Here’s an example that denotes both package-level, and block-level variable declarations:
package main
import (
"fmt"
)
// t is a package-level variable that can be accessed
// anywhere in the `main` package. You can say that it is
// global to the package
var t = true
func main() {
// f is a block-level variable accessible from its point of declation to
// to the end of the function
f := false
{
// i is a block-level variable that's only valid from this point
// until the end of the block
i := 20
fmt.Println(i) // 20
} // this block scope is now over so i is no longer valid
fmt.Println(t, f) // true false
}
If you’ve worked with a lexically scoped programming language before, this behavior should be familiar to you. Aside from explicit blocks that are clearly defined with a matching pair of curly braces, Go has a few implicit blocks that you should be aware of. For example, each clause in a switch
or select
statement is also an implicit block even though curly braces are not present:
func main() {
s := "world"
switch s {
case "hello":
// i can be accessed only within this clause
// and not the whole switch block
i := 10
fmt.Println(i)
case "world":
// i is out of scope here so this code will fail to compile
fmt.Println(i)
}
}
Another example is in if
statements. This is one you are likely to encounter more frequently in Go programs:
package main
import (
"errors"
"fmt"
)
func main() {
// this `err` variable is scoped to the whole
// function
err := errors.New("An error")
// this `err` variable is scoped to only this `if`
// block (and `else` blocks if any). It shadows the
// previous `err` variable
if err := fmt.Errorf("Another error"); err != nil {
fmt.Println(err) // Another error
} // `err` goes out of scope here
fmt.Println(err) // An error
}
As an aside, the var
declaration syntax may also be used to declare a block-level variables, but it’s typically only used when you want to initialise a variable to its zero value, then assign to it later.
func main() {
var s string // s is initialised to its zero value which is ""
s = "foo" // assignment occurs later in the function
}
Shadowing is a feature in Go that allows you to declare a variable in one block, and declare another variable with the same name in an inner block. Here’s an example:
func main() {
str := "world"
{
str := "hello" // outer str is inaccessible from this point
fmt.Println(str) // hello
}
fmt.Println(str) // world
}
The first str
variable declared in the main function is assigned the string value “world”. In an inner block, a second str
variable is declared and assigned the string value of “hello”. Whenever you access the str
variable in the inner block, it will always refer to the inner declaration. So we say that the first str
variable is shadowed by the second.
Note that the inner str
does not affect the outer str
in any way. In fact, the inner variable does not have to be of the same type as the outer one because they are actually completely different from each other. They just happen to share the same name. Keep in mind that due to this behaviour you will not be able to access the outer str
from the inner block unless you change its name.
Variable shadowing does not apply if you attempt to redeclare a variable within the same block. The program will fail to compile:
func main() {
s := "world"
s := "hello" // no new variables on left side of :=
}
However, the program below will compile and run just fine.
func main() {
var a, b int
c, a, b := 3, 1, 2
fmt.Println(a, b, c) // 1 2 3
}
Although it appears as if the a
and b
variables are being redeclared on the second line of the main
function, that’s actually not the case. What happens here is that only the c
variable is declared while the other two are assigned to. This reason is that the :=
operator does always declare all the variables on its left-hand side.
At least one new variable needs to be present when using the short declaration syntax for this to work though. If we remove the c
variable declaration from the above snippet, the code will fail to compile as before:
func main() {
var a, b int
a, b := 1, 2 // no new variables on left side of :=
fmt.Println(a, b)
}
In this article, you learned how to create and assign variables in Go, what zero values are and how variable scoping and shadowing work in the language. Now that you’ve explored how variables work, the next article will look at the data types they can have.
Thanks for reading, and happy coding!
Comments
Ground rules
Please keep your comments relevant to the topic, and respectful. I reserve the right to delete any comments that violate this rule. Feel free to request clarification, ask questions or submit feedback.