Variables and types
Go is a statically-typed language, which means that variables have a fixed type that cannot be changed at runtime. Go has a number of built-in types, including:
bool
: A boolean type representing true or false.int
,int8
,int16
,int32
,int64
: Integer types of different sizes.uint
,uint8
,uint16
,uint32
,uint64
: Unsigned integer types of different sizes.float32
,float64
: Floating point types of different sizes.complex64
,complex128
: Complex types of different sizes.string
: A string type representing a sequence of Unicode characters. You can declare a variable with a specific type using thevar
keyword:
In Go, variables are declared using the var
keyword, followed by the variable name and the type. For example:
var x int
var y string
You can also declare and initialize variables in one line using the :=
operator:
x := 5
y := "hello"
Go has a number of built-in types, including integers, floating-point numbers, strings, and boolean values. You can also create your own types using structs. Example: Here is an example of declaring and using variables in Go:
package main
import "fmt"
func main() {
// Declare and initialize variables using the `var` keyword
var x int = 10
var y string = "hello"
var z bool = true
// Declare and initialize variables using the `:=` operator
a := 5
b := "world"
c := false
fmt.Println(x, y, z)
fmt.Println(a, b, c)
}
Output:
10 hello true
5 world false
In Go, variables can be of any type, including basic types, arrays, slices, maps, and structs. Go also has a number of type aliases, such as byte
for uint8
, rune
for int32
, and error
for a type representing an error value.
Go has a number of type conversions, which allow you to convert a value from one type to another. For example, you can use the int
function to convert a float64
to an int
:
x := 3.14
y := int(x)
This converts the float64
value x
to an int
value y
.
In addition to the built-in types, Go also allows you to define your own types using the type
keyword. For example, you can define a type alias for an existing type:
type MyInt int
This defines a new type MyInt
that is an alias for the int
type. You can use MyInt
like any other type:
var x MyInt = 10
You can also define a new type based on a struct or an interface. For example:
type Point struct {
X int
Y int
}
This defines a new type Point
based on a struct with two fields X
and Y
, both of type int
. You can create a value of this type using the struct
keyword:
p := Point{1, 2}
You can access and modify the fields of a struct using the dot operator .
:
p.X = 3
fmt.Println(p.Y)
You can also use the new
function to create a pointer to a struct value:
q := new(Point)
q.X = 4
q.Y = 5
In this case, q
is a pointer to a Point
struct value. Example: Here is an example of defining a new type based on a struct:
package main
import "fmt"
type Point struct {
X int
Y int
}
func main() {
p := Point{1, 2}
q := new(Point)
q.X = 4
q.Y = 5
fmt.Println(p)
fmt.Println(q)
}
Output:
{1 2}
&{4 5}
Constants
Go has a built-in type called const
for representing constant values. A constant is a value that cannot be changed at runtime. Constants can be of any type, including basic types, arrays, and structs. Constants are declared using the const
keyword:
const Pi = 3.14
This declares a constant Pi
of type float64
with the value 3.14. You can also specify the type of a constant explicitly:
const Pi float64 = 3.14
You can use the iota
keyword to define a series of constants:
const (
Monday = iota
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
)
This defines a series of constants Monday
, Tuesday
, Wednesday
, etc., with the values 0, 1, 2, etc. iota
is a predefined identifier that represents successive integer values in a constant declaration. The value of iota
is reset to 0 whenever the const
keyword is encountered. You can also use iota
with expressions:
const (
One = 1 << iota
Two
Four
Eight
)
This defines a series of constants One
, Two
, Four
, etc., with the values 1, 2, 4, etc. The <<
operator is the bit shift operator, which shifts the bits of a value to the left. Example: Here is an example of using constants in Go:
package main
import "fmt"
const Pi = 3.14
const (
Monday = iota
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
)
const (
One = 1 << iota
Two
Four
Eight
)
func main() {
fmt.Println(Pi)
fmt.Println(Monday)
fmt.Println(One)
fmt.Println(Eight)
}
Output:
3.14
0
1
8
Constants are useful for defining values that are used throughout your code, such as mathematical constants, configuration parameters, and other values that should not change. They can also be used to define enums, which are a way to define a set of named constants.
In Go, constants are evaluated at compile-time, so they are not stored in memory at runtime. This means that you cannot take the address of a constant or use a constant as a pointer. Constants can also be used in place of named variables in expressions, but not in place of the keyword var
or the :=
operator.
Functions
In Go, functions are declared using the func
keyword, followed by the function name, a list of parameters, and the return type. For example:
func add(x int, y int) int {
return x + y
}
You can also define functions with multiple return values:
func divmod(x int, y int) (int, int) {
div := x / y
mod := x % y
return div, mod
}
To call a function, you can use its name and pass in the required arguments. For example:
result := add(5, 6)
div, mod := divmod(10, 3)
You can also define function arguments with default values by using the name=value
syntax. For example:
func greet(name string, greeting string = "Hello") string {
return greeting + " " + name
}
In this case, the greeting
argument has a default value of “Hello”, so if it is not specified when the function is called, it will use the default value. Example: Here is an example of using functions in Go:
package main
import "fmt"
func add(x int, y int) int {
return x + y
}
func divmod(x int, y int) (int, int) {
div := x / y
mod := x % y
return div, mod
}
func greet(name string, greeting string = "Hello") string {
return greeting + " " + name
}
func main() {
fmt.Println(add(5, 6))
fmt.Println(divmod(10, 3))
fmt.Println(greet("John"))
fmt.Println(greet("Jane", "Hi"))
}
Output
11
3 1
Hello John
Hi Jane
Control structures
Go has a number of control structures for controlling the flow of a program. These include:
if/else
statements:
These are used to execute a block of code if a certain condition is true, and a different block of code if the condition is false. For example:
if x > y {
fmt.Println("x is greater than y")
} else {
fmt.Println("x is not greater than y")
}
You can also use else if
clauses to test multiple conditions:
if x > y {
fmt.Println("x is greater than y")
} else if x < y {
fmt.Println("x is less than y")
} else {
fmt.Println("x is equal to y")
}
for
loops:
Go has two types of for loops: a traditional for
loop and a range
loop. The traditional for
loop has the following syntax:
for i := 0; i < 10; i++ {
fmt.Println(i)
}
The range
loop is used to iterate over arrays, slices, maps, and strings. It has the following syntax:
numbers := []int{1, 2, 3, 4, 5}
for i, v := range numbers {
fmt.Println(i, v)
}
This will print the index and value of each element in the numbers
slice. You can also use the break
and continue
statements to control the flow of a loop. The break
statement will exit the loop, while the continue
statement will skip the rest of the current iteration and move on to the next one. Example: Here is an example of using for
loops and control statements in Go:
package main
import "fmt"
func main() {
// Traditional for loop
for i := 0; i < 5; i++ {
fmt.Println(i)
}
// Range loop
numbers := []int{1, 2, 3, 4, 5}
for i, v := range numbers {
fmt.Println(i, v)
}
// Break statement
for i := 0; i < 5; i++ {
if i == 3 {
break
}
fmt.Println(i)
}
// Continue statement
for i := 0; i < 5; i++ {
if i == 3 {
continue
}
fmt.Println(i)
}
}
Output:
0
1
2
3
4
0 1
1 2
2 3
3 4
4 5
0
1
2
0
1
2
4
switch
statements:
Switch statements are used to execute a block of code based on the value of a variable. They have the following syntax:
switch x {
case 1:
fmt.Println("x is 1")
case 2:
fmt.Println("x is 2")
default:
fmt.Println("x is not 1 or 2")
}
You can also use the fallthrough
keyword to execute the code for the next case, regardless of whether the case condition is true. Example: Here is an example of using switch
statements in Go:
package main
import "fmt"
func main() {
x := 2
switch x {
case 1:
fmt.Println("x is 1")
case 2:
fmt.Println("x is 2")
fallthrough
case 3:
fmt.Println("x is 3")
default:
fmt.Println("x is not 1, 2, or 3")
}
}
Output:
x is 2
x is 3
You can also use switch
statements with conditions, using the syntax case x == y:
. For example:
switch {
case x > y:
fmt.Println("x is greater than y")
case x < y:
fmt.Println("x is less than y")
default:
fmt.Println("x is equal to y")
}
In this case, the switch
statement will evaluate each case condition and execute the corresponding block of code if the condition is true.
Pointers
Pointers are a way to store the memory address of a value. In Go, pointers are represented using the *
operator. You can declare a pointer to a value using the *
operator:
var x int = 10
var p *int = &x
This declares a variable p
of type *int
, which is a pointer to an int
value. The &
operator is the address-of operator, which returns the memory address of a value. You can dereference a pointer using the *
operator:
fmt.Println(*p)
This prints the value of x
through the pointer p
. You can also use the *
operator to modify the value of a pointer:
*p = 20
fmt.Println(x)
This modifies the value of x
through the pointer p
. You can use the new
function to create a pointer to a value:
q := new(int)
*q = 30
fmt.Println(*q)
This creates a new int
value and stores it in q
, which is a pointer to the value. Example: Here is an example of using pointers in Go:
package main
import "fmt"
func main() {
var x int = 10
var p *int = &x
*p = 20
fmt.Println(x)
q := new(int)
*q = 30
fmt.Println(*q)
}
Output:
20
30
Pointers are a powerful feature of Go that allow you to manipulate values indirectly, through their memory addresses. They can be used to pass values by reference, to avoid copying large values, and to implement complex data structures such as linked lists and trees. However, they can also be confusing and error-prone, especially for new Go programmers. It is important to use pointers carefully and understand their behavior.
In Go, pointers are always of a fixed size, regardless of the type of the value they point to. This means that you can store a pointer to any value in a variable of type *int
, for example. However, you cannot use a pointer to a value of a different type. For example, you cannot use a *string
pointer to modify an int
value. Pointers are also not compatible with the arithmetic and comparison operators, with the exception of the ==
and !=
operators, which can be used to compare the values of two pointers.
Go has a built-in type called struct
for defining composite data types. A struct is a collection of fields, each with a name and a type. For example:
type Point struct {
X int
Y int
}
This defines a new struct type Point
with two fields X
and Y
, both of type int
. You can create a new struct value using the struct
keyword and the field values:
p := Point{1, 2}
You can access and modify the fields of a struct using the dot operator .
:
p.X = 3
fmt.Println(p.Y)
You can also use the new
function to create a pointer to a struct value:
q := new(Point)
q.X = 4
q.Y = 5
In this case, q
is a pointer to a Point
struct value. You can define methods on structs by specifying a receiver type. A method is a function with a special receiver argument that is bound to the struct. For example:
func (p *Point) Distance() float64 {
return math.Sqrt(float64(p.X*p.X + p.Y*p.Y))
}
This defines a method Distance
on the Point
struct type with a receiver type of *Point
. The method returns the distance of the point from the origin using the Pythagorean theorem. You can call a method on a struct value or a pointer to a struct value using the dot operator:
fmt.Println(p.Distance())
fmt.Println(q.Distance())
This will print the distance of the points p
and q
from the origin. Example: Here is a complete example of using structs and methods in Go:
package main
import (
"fmt"
"math"
)
type Point struct {
X int
Y int
}
func (p *Point) Distance() float64 {
return math.Sqrt(float64(p.X*p.X + p.Y*p.Y))
}
func main() {
p := Point{1, 2}
q := new(Point)
q.X = 4
q.Y = 5
fmt.Println(p.Distance())
fmt.Println(q.Distance())
}
Output:
2.23606797749979
6.4031242374328485
Error Handling
In Go, errors are represented using the error
type, which is a built-in interface with a single method:
type error interface {
Error() string
}
The Error
method returns a string description of the error. You can use the error
type to represent any error that may occur in your program. For example:
if err != nil {
return err
}
This returns the err
value as an error
if it is not nil
. You can create a new error value using the errors.New
function:
err := errors.New("invalid input")
This creates a new error value with the message “invalid input”. You can also use the fmt.Errorf
function to create a formatted error message:
err := fmt.Errorf("invalid input: %v", input)
This creates a new error value with the message “invalid input: input”, where input
is the value of the input
variable. You can check if an error value is nil
to determine if an error has occurred:
if err != nil {
fmt.Println(err)
}
This prints the error message if err
is not nil
. You can use the defer
keyword to ensure that a function is always executed, even if an error occurs:
func foo() error {
file, err := os.Open("file.txt")
if err != nil {
return err
}
defer file.Close()
// do something with file
return nil
}
This opens the “file.txt” file and ensures that it is always closed, even if an error occurs while opening the file.
Error handling is an important part of programming in Go. It allows you to handle and recover from errors that may occur during the execution of your program. You can use the error
type and the errors
and fmt
packages to create and manipulate error values, and the defer
keyword to ensure that necessary actions are always taken.
Packages and Importing
In Go, a package is a collection of related Go source files that are compiled and installed together. A package usually consists of one or more Go source files located in the same directory and sharing the same package name.
To use a package in your Go program, you need to import it first. You can import a package using the import
keyword:
import "fmt"
This imports the fmt
package, which provides formatting and I/O functions. You can import multiple packages at once:
import "fmt"
import "math"
You can also use the import
keyword with the .
notation to import all the identifiers in a package into the current namespace:
import . "fmt"
This imports all the identifiers in the fmt
package into the current namespace, so you don’t have to use the fmt.
prefix to access them. You can also use the import
keyword with the _
notation to import a package for its side effects only, without accessing its identifiers:
import _ "database/sql"
This imports the database/sql
package, which initializes some global variables, but does not bring any of its identifiers into the current namespace. You can also use the import
keyword with the as
notation to give an imported package a different name in your program:
import f "fmt"
This imports the fmt
package as f
, so you have to use the f.
prefix to access its identifiers.
Packages and importing are important concepts in Go. Packages allow you to organize your Go source code and share it with others, and importing allows you to use the code in other packages in your program. You can use the import
keyword to import packages and access their identifiers, and the .
and _
notations to import packages for their side effects only or to give them different names.