2. Primitive Types & Declarations
Literals
💡 Go assigns a default zero value to any variable that is declared but not assigned a variable - this means NOTHING can ever be undefined
and removes a source of bugs found in C and C++ programs.
There are 4 common kinds of literals in Go (and a fifth rare one to do with complex numbers).
Integers
- Usually base 10, but other prefixes can be used to indicate other bases:
0b
for binary (base 2),0o
for octal (base 8) or0x
for hexadecimal (base 16). If you use0
without a leading letter it is an octal literal, but this is generally considered a confusing way of declaring. - You can put underscores in between numbers to make it easier to read, e.g.
1_000_000
is the same as1000000
.
Floating point
- Have decimal points to indicate the fractional portion of the value
- Can also have an exponent specified with the letter e and a positive or negative number, e.g.
6.03e23
Rune
- Represent characters are are surrounded by single quotes. Single and double quotes are NOT interchangeable in Go. Runes can be written as:
- Unicode characters
’a’
, - 8-bit octal numbers
’\141’
, - 8-bit hexadecimal numbers
’\x61’
, - 16-bit hexadecimal numbers
’\u0061’
, - 32-bit Unicode numbers
’\U00000061’
- There are a few backslash escape rune literals too, like newline
'\n'
, tab\t
, single quote\'
, double quote\"
and backslash\\
- Unicode characters
Strings
- Double quotes create an interpreted string literal, e.g.
"Greetings"
. These contain zero or more rune literals in any of the forms allowed. The only characters that cannot appear are unescaped backslashes, unescaped newlines and unescaped double quotes. - It’s usually better however to use raw string literals to write multiline strings, like:
`Greetings and
Saluations`
Booleans
The bool can only be true or false. The zero value for a bool is false.
var flag bool // defaults to false
var isTrue = true // set to true
Numeric Types
Integers
There are signed and unsigned integers in a variety of sizes, from one to eight bytes (or from 8 to 64 bits). The zero value for all integers is 0.
Type | Min Value | Max Value |
---|---|---|
int8 | -128 | 127 |
int16 | -32,768 | 32,767 |
int32 | -2,147,483,648 | 2,147,483,647 |
int64 | -9,223,372,036,854,775,808 | 9,223,372,036,854,775,807 |
uint8 | 0 | 255 |
uint16 | 0 | 65,535 |
uint32 | 0 | 4,294,967,295 |
uint64 | 0 | 18,446,744,073,709,551,615 |
Go has some special integer types:
byte
is identical touint8
and it’s more common to call it abyte
in Go code.int
is identical toin32
on a 32-bit CPU andint64
on a 64-bit CPU. Since it isn’t consistent from platform to platfrm, it is a compile-time error to assign, compare or perform mathematical operations between anint
,int32
andint64
without a type conversion.uint
follows the same rules asint
rune
is an alias forint32
uintptr
which will be discussed later.
Choosing which type to use:
- When working with binary file format or network protocol that has integers of a specific size or sign, use the same type as that.
- If you’re writing a library function that should work with any integer, write a pair of functions - one with
int64
for the params & variables and one withuint64
. - In all other cases, just use
int
Arithmetic Operators
There are the usual operators +
, -
, *
, /
and %
for modulus.
You can combine any of the operators with =
to modify the variable: +=
, -=
, *=
. /=
, and %=
var x int = 10
x *= 2 // x = 20
Comparison operators
- Equals
==
- Not Equals
!=
- Greater Than
>
- Greater Than or Equals
>=
- Less Than
<
- Less Than or Equals
<=
Bit manipulation
- Bit shift left
<<
- Bit shift right
>>
- Logical AND
&
- Logical OR
|
- Logical XOR
^
- Logical AND NOT
&^
We can also combine them with equals to modify a variable, like &=
, |=
, &^=
, <<=
, >>=
Floating Point Types
Like integer types, the zero value for floats is 0.
Type | Min Value | Max Value | Smallest Non-Zero Value |
---|---|---|---|
float32 | -3.4e+38 | 3.4e+38 | 1.4e-45 |
float64 | 1.7e+308 | 1.7e+308 | 4.9e-324 |
Float defaults to float64
which mitigates floating point accuracy issues since float32
only has ~6-7 decimal digits of precision.
In most cases, however, you should use integers as it’s much safer - floats aren’t exact.
Operators
- You can use all the above operators except
%
. - Dividing a non-zero floating point variable by 0 returns
+Inf
or-Inf
- Diving a floating point variable set to 0 by 0 returns
NaN
DO NOT USE ==
and !=
to compare floats, due to the inexact nature of them, they might not be equal when they should be. You could use a maximum allowed variance (epsilon) to determine if the difference between the floats is less than that instead.
Strings
The zero value for a string is the empty string, ""
.
- You can use equality operators
==
,!=
- Or ordering with
>
,>=
,<
,<=
- They are concatenated using the
+
operator.
Explicit Type Conversion
We cannot automatically convert from one type to another when needed implicilty, we have to explicitly declare the type conversion.
var x int = 10
var y float64 = 30.2
var z float64 = float64(x) + y
var d int = x + int(y)
fmt.Println(z, d) // 40.2, 40
This also means you can’t treat another Go type as a Boolean. There is no concept of “truthy” and “falsey” values in Go - if you want to convert another type to boolean you must use one of the comparison operators (==
, !=
, >
, …). For example to check if x is equal to 0, you have to write x == 0
or to check if a string is empty, s == ""
.
Var versus :=
There are many ways to declare variables in Go:
var x int = 10 // the most verbose
var x = 10 // gets the type int
var x int // gets the value 0
var x, y int = 10, 20 // x = 10, y = 20
var x, y = 10, "hello" // different types
// declaring multiple variables at once:
var (
x int
y = 20
z int = 30
d, e = 40, "hello"
f, g string
)
There is also the short declaration format, with :=
. When you are within a function, you can use :=
instead of var
and it uses type inference. These 2 are the same thing:
var x = 10
x := 10
var x, y = 10, "hello"
x, y := 10, "hello"
You can also re-assign values to existing variables
x := 10
x, y := 30, "hello"
The only restriction is that you HAVE to be within a function, you can’t use :=
at the package level - only var
.
When you should avoid using :=
:
- When initializing a variable to its zero value, use
var x int
for clarity - When assigning an untped constant or literal to a variable and the default type isn’t the type you want. You can write
x := byte(20)
but it’s idiomatic to writevar x byte = 20
- Because
:=
allow you to assign new and existing variables, it sometimes creates new variables when you think you are reusing existing ones. In those situations, explicitly declare all the variables withvar
and use the assignment operator (=
) to assign values to old and new variables.
Using const
To ensure a variable is immutable, we use const
.
const x int64 = 10
const (
idKey = "id"
nameKey = "name"
)
const z = 20 * 10
func main() {
const y = "hello"
x = x + 1
y = "bye"
}
This will throw two errors - cannot assign to x
and cannot assign to y
However, it is very limited. They can only hold values that the compiler can figure out at compile time - which limits it to:
- Numeric literals
- Booleans
- Strings
- Runes
- The built in functions
complex
,real
,imag
,len
andcap
- Expressions that consist of operators and the preceding values
Go doesn’t provide a way to specify that a value calculated at runtime is immutable, this means there are no immutable arrays, slices, maps or structs and there’s no way to declare that a field in a struct is immutable.
Typed and Untyped Constants
Constants can be typed or untyped, they behave exactly like a literal - if it has no type of it’s own, it will have a default type that is used when no other type can be inferred.
- If you are giving a name to a mathematical constant that could be used with multiple numeric types, then keep the constant untyped
const x = 10 // untyped
All the following are legal:
var y int = x
var z float64 = x
var d byte = x
If we wanted to be strict with the types, we could say const x int = 10
and only int can be assigned to this.
Unused Variables
Go has some unique rules to ensure large teams can collaborate on Go and keep the coding style consistent. One of these is that every declared local variable MUST be read. Otherwise this will throw a compile-time error.
However, the compiler won’t stop you from using unread package-level variables, which is another reason you shouldn’t use these.
Naming Conventions
Go requires variable names to:
- Start with a letter or underscore
- Only contain numbers, underscores & letters
Any Unicode character that is considered a number or letter is allowed.
You should AVOID names like these:
_0 := 0_0
_𝟙 := 20
η := 3
a := "hello" // unicode U+FF41
While they work, they are non-idiomatic and are confusing or difficult to type, because a
and a
look almost exactly the same, but will be considered different variables.
Common rules:
k
andv
are used for key and value in a for -range loopi
andj
are used for indexes in a standard for loop- constants are NOT written in all caps (like INDEX_COUNTER).