Go foundation
Variables
We use the keyword var to define a variable. The variable type comes after the variable name.
Variable declaration Shortcut
:=
can only be used inside functions, for defining global variables we have to stick to using var. It throws a syntax error otherwise.
Blank variable
_
is called the blank variable and it is used to ignore a value.
Suppose we have a function divide, which takes two arguments, and will return the quotient and remainder of arg1 divided by arg2. But we only want the remainder, so we ignore the quotient using
Unused variables cause compilation errors. Compile the following code and see what happens. Sometimes, we use this notations to import libraries which we do not want to use as prefix, while working with databases we do not want to prefix the library name to each function call of the library, so we use the blank variable. We will see that in a future chapter.
Constants
Constants are the values that are determined during compile time that cannot be changed during runtime. In Go, you can use number, boolean or string as types of constants.
Defining constants
More examples.
Elementary types
Boolean
The keyword bool
is used to define a variable of the boolean type. There are two boolean values, true
or false
; false
will be the default value. Unlike other languages, Go doesn't allow us to convert boolean into number.
Numerical types
Integers
Integer types include signed and unsigned integer types, int
and uint
respectively. They have the same length, but the specific length depends on your operating system. They use 32-bit in 32-bit operating systems, and 64-bit in 64-bit operating systems. Go also has types that have specific length including rune
, int8
, int16
, int32
, int64
, byte
, uint8
, uint16
, uint32
, uint64
. Note that rune
is alias of int32
and byte
is alias of uint8
.
Go doesn't allow assigning values between data types. The following operation will cause compilation errors.
Although int32 has a longer length than int8, and has the same type as int, you cannot assign values between them. ( c will be asserted as type int
here )
Fractions
Float types have the float32
and float64
types and no type called float
. The latter one is the default type if using brief statement.
Complex numbers
Go supports complex numbers as well. complex128
(with a 64-bit real and 64-bit imaginary part) is the default type, if you need a smaller type, there is one called complex64
(with a 32-bit real and 32-bit imaginary part). Its form is RE+IMi
, where RE
is real part and IM
is imaginary part, the last i
is the imaginary number. There is a example of complex number.
String
Go uses the UTF-8 character set. Strings are represented by double quotes ""
or backticks ``
.
String objects do not now allow value change. You will get errors when you compile the following code.
For changing a string, a new string has to be created using the old string. Go doesn't allow modifications to a string variable, but you can always create a new one.
+
operator can be used to concatenate two strings.
and also.
` (backtick) is used to have a multiple-line string.
` (backtick) will not escape any characters in a string.
Error types
Go doesn't have the try catch block. There is one error
type for the purpose of dealing with errors in the package called errors
. Go requires us to explicitly handle our errors or ignore them. Typical error handling forms a if err != nil
ladder.
Underlying data structures
The following picture comes from an article about Go data structure in Russ Cox's Blog. As you can see, Go utilizes blocks of memory to store data.
Important points
Define by group
If you want to define multiple constants, variables or import packages, you can use the group form.
Basic form.
Group form.
iota enumerate
Go has a keyword called iota
, this keyword is to make enum
, it begins with 0
, increased by 1
.
Some rules
Go is concise because it has some default behaviors.
Any variable that begins with a capital letter means it will be exported, private otherwise.
The same rule applies for functions and constants, no
public
orprivate
keyword exist in Go.
array, slice, map
array
arr
is an array, we define one as follows.
in [n]type
, n
is the length of the array, type
is the type of its elements. Like other languages, we use []
to get or set element values within arrays.
Since length is a part of the array type, [3]int
and [4]int
are different types. We cannot change the length of arrays.
Arrays are passed as copy rather than references when passed to a function as arguments. For references, we have to use slice
. More about slices below.
It's possible to use :=
when you define arrays.
You may want to use arrays as arrays' elements. Let's see how to do this.
Array underlying data structure.
slice
The main disadvantage of arrays is that we need to know the size prehand. At times this isn't possible. For this, we need a "dynamic array". This is called a slice
in Go.
slice
is not really a dynamic array
. It's a reference type. slice
points to an underlying array
whose declaration is similar to array
, but doesn't need length.
Then we define a slice
, and initialize its data.
slice
can redefine existing slices or arrays. slice
uses array[i:j]
to slice, where i
is the start index and j
is end index, but notice that array[j]
will not be sliced since the length of the slice is j - i
.
Notice the differences between slice
and array
when you define them. We use […]
to let Go calculate length but use []
to define slice only. Slices do not have length, slices point to arrays which have lengths.
Their underlying data structure.
slice has some convenient operations.
slice
is 0-based,ar[:n]
equals toar[0:n]
The second index will be the length of
slice
if omitted,ar[n:]
equals toar[n:len(ar)]
.You can use
ar[:]
to slice whole array, reasons are explained in first two statements.
More examples:
slice
is a reference type, so any changes will affect other variables pointing to the same slice or array. For instance, in the case of aSlice
and bSlice
above, if you change the value of an element in aSlice
, bSlice
will be changed as well.
slice
is like a struct by definition and it contains 3 parts.
A pointer that points to where
slice
starts.The length of
slice
.Capacity, the length from start index to end index of
slice
.
The underlying data structure of the code above as follows.
There are some built-in functions for slice.
len
gets the length ofslice
.cap
gets the maximum length ofslice
append
appends one or more elements toslice
, and returnsslice
.copy
copies elements from one slice to the other, and returns the number of elements that were copied.
Attention: append
will change the array that slice
points to, and affect other slices that point to the same array.
Also, if there is not enough length for the slice ((cap-len) == 0
), append
returns a new array for this slice. When this happens, other slices pointing to the old array will not be affected.
map
Map is a key value pair, like a dictionary in Python. Use the form map[keyType]valueType
to define it.
The 'set' and 'get' values in map
are similar to slice
, however the index in slice
can only be of type 'int' while map
can use much more than that: for example int
, string
, or whatever you want. Also, they are all able to use ==
and !=
to compare values.
Some notes when you use map.
map
is disorderly. Everytime you printmap
you will get different results. It's impossible to get values byindex
. You have to usekey
.map
doesn't have a fixed length. It's a reference type just likeslice
.len
works formap
also. It returns how manykey
s that map has.It's quite easy to change the value through
map
. Simply usenumbers["one"]=11
to change the value ofkey
one to11
.
You can use form key:val
to initialize map's values, and map
has built-in methods to check if the key
exists.
Use delete
to delete an element in map
.
As I said above, map
is a reference type. If two map
s point to same underlying data, any change will affect both of them.
make, new
make
does memory allocation for built-in models, such as map
, slice
, and channel
, while new
is for types' memory allocation.
new(T)
allocates zero-value to type T
's memory, returns its memory address, which is the value of type *T
. By Go's definition, it returns a pointer which points to type T
's zero-value.
new
returns pointers.
The built-in function make(T, args)
has different purposes than new(T)
. make
can be used for slice
, map
, and channel
, and returns a type T
with an initial value. The reason for doing this is because the underlying data of these three types must be initialized before they point to them. For example, a slice
contains a pointer that points to the underlying array
, length and capacity. Before these data are initialized, slice
is nil
, so for slice
, map
and channel
, make
initializes their underlying data and assigns some suitable values.
make
returns non-zero values.
The following picture shows how new
and make
are different.
Zero-value does not mean empty value. It's the value that variables default to in most cases. Here is a list of some zero-values.
Links
Last updated