Theme NexT works best with JavaScript enabled
0%

Go Manual


IMPORTANT:
Some of the content here is a personal summary/abbreviation of contents on the Offical Go Documentation. Feel free to refer to the official site if you think some of the sections written here are not clear.


Installing Go

Please follow the Offical Guide on Installing Go.

Packages

(additional reference: https://www.callicoder.com/golang-packages/#:~:text=Go%20Package,following%20syntax%20%2D%20package)

Every Go program is made up of packages, and every Go source file belongs to a package. To declare a source file to be part of a package, we use the following syntax -

1
package <packagename>

The above package declaration must be the first line of code in your Go source file. All the functions, types, and variables defined in your Go source file become part of the declared package.

You can choose to export a member defined in your package to outside packages, or keep them private to the same package. Other packages can import and reuse the functions or types that are exported from your package.

Let’s see an example

Almost all the code that we will see in this tutorial series include the following line -

1
import "fmt"

fmt is a core library package that contains functionalities related to formatting and printing output or reading input from various I/O sources. It exports functions like Println(), Printf(), Scanf() etc, for other packages to reuse.

Note:

  • By convention, the package name is the same as the last element of the import path. For instance, the "math/rand" package comprises files that begin with the statement package rand.

The main package and main() function

Go programs start running in the main package. It is a special package that is used with programs that are meant to be executable. The main() function is a special function that is the entry point of an executable program.

By convention, Executable programs (the ones with the main package) are called Commands. Others are called simply Packages.

Usually, you would have a main package at the top level of your project, and then create sub-directories for other packages. For example, you could have the following structure:

1
2
3
4
5
6
7
myProject/
main.go
subModule/
test1.go
test2.go
anotherModule/
test3.go

Running a Program

Programs start running in package main.

For example, using the math/rand pacakge, we could generate a random number by:

1
2
3
4
5
6
7
8
9
10
11
package main

import (
"fmt"
"math/rand"
)

func main() {
fmt.Printf("hello, world\n")
fmt.Println(rand.Intn(10))
}

Note:

  • The environment in which these programs are executed is deterministic, so each time you run the example program rand.Intn will return the same number. To see a different number, seed the number generator; see rand.Seed.

Imports

To import a go package (a collection of go files), it first needs to be included in your GoPath.

There are two ways to import packages in Go -

1
2
3
4
5
6
7
8
9
10
11
12
// Multiple import statements
import "fmt"
import "time"
import "math"
import "math/rand"
// Factored import statements
import (
"fmt"
"time"
"math"
"math/rand"
)

It is generally considered a good style to use the factored import statement.

Notice that the imported package name basically follow the directory hierarchy (as mentioned before, Go’s convention is that - the package name is the same as the last element of the import path). For example, the name of the package imported as math/rand is rand. It is imported with path math/rand because It is nested inside the math package as a subdirectory.

Import Paths

(additional reference: https://www.callicoder.com/golang-packages/)

All import paths are relative to the GoPath or GoModule (this will be talked about later) path.

For example, if you have github.com/callicode/packer as the directory where you initiate your go.module file, then you can refer to packages relative to that module’s directory:

1
2
3
4
5
import (
"github.com/callicoder/packer/numbers"
"github.com/callicoder/packer/strings"
"github.com/callicoder/packer/strings/greeting"
)

Package Alias

You can use package alias to resolve conflicts between different packages of the same name, or just to give a short name to the imported package

1
2
3
import (
str "strings" // Package Alias, so that you can use str.some_function() for strings.some_function()
)

Adding 3rd party Packages

Adding 3rd party packages to your project is very easy with Go modules. You can just import the package to any of the source files in your project, and the next time you build/run the project, Go automatically downloads it for you -

1
2
3
4
5
6
7
8
9
10
package main

import (
"fmt"
"rsc.io/quote" // a third-party package
)

func main() {
fmt.Println(quote.Go())
}

And the output will look like:

1
2
3
4
5
6
7
8
9
10
11
12
$ go run main.go
go: finding rsc.io/quote v1.5.2
go: downloading rsc.io/quote v1.5.2
go: extracting rsc.io/quote v1.5.2
go: downloading rsc.io/sampler v1.3.0
go: extracting rsc.io/sampler v1.3.0
go: downloading golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
go: extracting golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
go: finding rsc.io/sampler v1.3.0
go: finding golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c

Don't communicate by sharing memory, share memory by communicating.

Go will also add this new dependency to the go.mod file.

Exports

In Go, a function/variable/type is exported if its name begins with a capital letter. For example, Pizza() is an exported name, as is Pi, which is exported from the math package.

pizza and pi do not start with a capital letter, so they are not exported. This means that they are not accessible from outside the package when using imports.

So, in short:

  • Anything (variable, type, or function) that starts with a capital letter is exported, and visible outside the package.
  • Anything that does not start with a capital letter is not exported, and is visible only inside the same package.

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"fmt"
"math"
)

func main() {
// MaxInt64 is an exported name
fmt.Println("Max value of int64: ", int64(math.MaxInt64))

// Phi is an exported name
fmt.Println("Value of Phi (ϕ): ", math.Phi)

// pi starts with a small letter, so it is NOT exported
fmt.Println("Value of Pi (𝜋): ", math.pi)
}
1
2
3
# Output
./exported_names.go:16:38: cannot refer to unexported name math.pi
./exported_names.go:16:38: undefined: math.pi

Functions

(additional reference: https://www.callicoder.com/golang-functions/)

In Golang, we declare a function using the func keyword. A function has a name, a list of comma-separated input parameters along with their types, the** result type(s), and a **body.

Following is an example of a simple function called avg that takes two input parameters of type float64 and returns the average of the inputs. The result is also of type float64 -

1
2
3
func avg(x float64, y float64) float64 {
return (x + y) / 2
}

Notice that the type comes after the variable name.

Function Parameters and Return Types

The input parameters and return type(s) are optional for a function. A function can be declared without any input and output.

The main() function is an example of such a function -

1
2
func main() {
}

Here is another example -

1
2
3
func sayHello() {
fmt.Println("Hello, World")
}

Input Type Abbreviation

You need to specify the type only once for multiple consecutive parameters of the same type

If a function has two or more consecutive parameters of the same type, then it suffices to specify the type only once for the last parameter of that type.

For example, we can declare the avg function that we saw in the previous section like this as well -

1
2
func avg(x, y float64) float64 { }
// Same as - func avg(x float64, y float64) float64 { }

Here is another example -

1
2
func printPersonDetails(firstName, lastName string, age int) { }
// Same as - func printPersonDetails(firstName string, lastName string, age int) { }

Multiple Return Results

Go is capable of returning multiple results. You just need to specify the return types separated by comma inside parentheses, and then return multiple comma-separated values from the function.

1
2
3
4
5
6
7
8
9
10
11
12
package main

import "fmt"

func swap(x, y string) (string, string) { // returning two strings
return y, x
}

func main() {
a, b := swap("hello", "world") // notice the use of :=, the short declaration, which will come later in this guide
fmt.Println(a, b)
}

A common approach using this feature is to return an error value from a function if there could be an abnormal behavior with some input.

Let’s see an example - The getStockPriceChange function that we saw will return ±Inf (Infinity) if the prevPrice is 0. If you want to return an error instead, you can do so by adding another return value of type error and return the error value like so -

1
2
3
4
5
6
7
8
9
func getStockPriceChangeWithError(prevPrice, currentPrice float64) (float64, float64, error) {
if prevPrice == 0 {
err := errors.New("Previous price cannot be zero")
return 0, 0, err
}
change := currentPrice - prevPrice
percentChange := (change / prevPrice) * 100
return change, percentChange, nil
}

The error type is a built-in type in Golang. Go programs use error values to indicate an abnormal situation. Don’t worry if you don’t understand about errors for now. You’ll learn more about error handling in a later section.

Named return values

Go’s return values may be named. If so, they are treated as variables defined at the top of the function.

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func split(sum int) (x, y int) { // notice the specified type
x = sum * 4 / 9 // notice the change from := (short declaration) to = (assignment)
y = sum - x
return // naked return
}

func main() {
fmt.Println(split(17))
}

Note:

  • A good practise would be using those names to document the meaning of the return values.

A return statement without arguments returns the named return values. This is known as a “naked” return.

Naked return statements should be used only in short functions, as with the example shown here. They can harm readability in longer functions.

Blank Identifier

Sometimes you may want to ignore some of the results from a function that returns multiple values.

For example, Let’s say that you’re using the split function that we defined in the previous section, but you’re only interested in the final result of y, not the intermediate x.

Well, you can use a blank identifier instead -

1
_, y := split(17)

Q: Why can you not just declare a variable but not use it?

The problem is that in Go, you’ll be forced to use the variables you declared because Go doesn’t allow creating variables that you never use.

Variables

(additional reference: https://www.callicoder.com/golang-variables-zero-values-type-inference/)

Every program needs to store some data/information in memory. The data is stored in memory at a particular memory location.

A variable is just a convenient name given to a memory location where the data is stored. Apart from a name, every variable also has an associated type.

For example, Golang has a data type called int8. It represents 8-bit integers whose values can range from -128 to 127. It also defines the operations that can be done on int8 data type such as addition, subtraction, multiplication, division etc.

Declaring Variables

In Golang, We use the var keyword to declare variables -

1
2
3
var firstName string
var lastName string
var age int

You can also declare multiple variables at once like so -

1
2
3
4
5
var (
firstName string
lastName string
age int
)

You can even combine multiple variable declarations of the same type with comma -

1
2
3
4
var (
firstName, lastName string
age int
)

Zero Values

Any variable declared without an initial value will have a zero-value depending on the type of the variable-

Type Zero Value
bool false
string ””
int, int8, int16 etc. 0
float32, float64 0.0

The example below demonstrates the concept of zero values:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func main() {
var (
firstName, lastName string
age int
salary float64
isConfirmed bool
)

fmt.Printf("firstName: %s, lastName: %s, age: %d, salary: %f, isConfirmed: %t\n",
firstName, lastName, age, salary, isConfirmed)
}
1
2
# Output
firstName: , lastName: , age: 0, salary: 0.000000, isConfirmed: false

Variable Initialization

Here is how you can initialize variables during declaration -

1
2
3
var firstName string = "Satoshi"
var lastName string = "Nakamoto"
var age int = 35

You can also use multiple declarations like this -

1
2
3
4
5
var (
firstName string = "Satoshi"
lastName string = "Nakamoto"
age int = 35
)

Or even combine multiple variable declarations of the same type with comma and initialize them like so -

1
2
3
4
var (
firstName, lastName string = "Satoshi", "Nakamoto"
age int = 35
)

Note:

  • All these styles are allowed in both package level declaration and function level declaration

Type Inference

Although Go is a Statically typed language (a language is statically-typed if the type of a variable is known at compile-time instead of at run-time), It doesn’t require you to explicitly specify the type of every variable you declare.

When you declare a variable with an initial value, Golang automatically infers the type of the variable from the value on the right-hand side. So you need not specify the type when you’re initializing the variable at the time of declaration -

1
2
3
4
5
6
package main
import "fmt"
func main() {
var name = "Rajeev Singh" // Type declaration is optional here.
fmt.Printf("Variable 'name' is of type %T\n", name)
}
1
2
# Output
Variable 'name' is of type string

In the above example, Golang automatically infers the type of the variable as string from the value on the right-hand side. If you try to reassign the variable to a value of some other type, then the compiler will throw an error -

1
2
var name = "Rajeev Singh"  // Type inferred as `string`
name = 1234 // Compiler Error

This also means that type inference allows us to declare and initialize multiple variables of different data types in a single line like so -

1
2
3
4
5
6
7
8
9
package main
import "fmt"
func main() {
// Multiple variable declarations with inferred types. No type is explicitly specified here
var firstName, lastName, age, salary = "John", "Maxwell", 28, 50000.0

fmt.Printf("firstName: %T, lastName: %T, age: %T, salary: %T\n",
firstName, lastName, age, salary)
}
1
2
# Output
firstName: string, lastName: string, age: int, salary: float64

Short Variable Declarations

Inside a function, the := short assignment statement can be used in place of a var declaration with implicit/inferred type.

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func main() {
var i, j int = 1, 2
k := 3
c, python, java := true, false, "yes!" // all of them would work in a function level

fmt.Println(i, j, k, c, python, java)
}

Outside a function, every statement needs to begin with a keyword (var, func, and so on) and so the := construct is not available.

Scope of Variables

A var statement can be at package or function level.

  • If you defined a variable at the function level, then it is visible only inside that function.
  • If you defined a variable at the package level, then it is visible to all functions inside that package.

However, when you declared a variable inside a function with the same name as the variable defined in the package level, then, from that declaration onwards, the variable defined in the function will be used.

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import "fmt"

var(
age int8 = 10
)

func main(){
fmt.Println(age)
testScope
fmt.Println(age) // still 10
}

func testScope(){
fmt.Println("Inside testScope")

fmt.Println(age)
var age int8 = 20 // overrides the value in the function level
fmt.Println(age)

fmt.Println("Exited testScope")
}

Types

Go is a statically typed programming language. Every variable in Golang has an associated type.

Data types classify a related set of data. They define how the data is stored in memory, what are the possible values that a variable of a particular data type can hold, and the operations that can be done on them.

Basic Types

Go’s basic types are

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
bool

string

int int8 int16 int32 int64 // signed
uint uint8 uint16 uint32 uint64 uintptr // unsigned

byte // alias for uint8

rune // alias for int32
// represents a Unicode code point

float32 float64

complex64 complex128 // e.g. 0.867 + 0.5i. By default it will be type complex128

The example shows variables of several types, and also that variable declarations may be “factored” into blocks, as with import statements.

The int, uint, and uintptr types are usually 32 bits wide on 32-bit systems and 64 bits wide on 64-bit systems. When you need an integer value you should use int unless you have a specific reason to use a sized or unsigned integer type.

Byte and Rune

Golang has two integer type aliases called byte and rune that are aliases for uint8 and int32 data types respectively -

Type Alias For
byte uint8
rune int32

In Go, the byte and rune data types are used to distinguish characters from integer values.

Golang doesn’t have a char data type. It uses** byte and rune to represent character values. The **byte data type represents ASCII characters and the rune data type represents a more broader set of Unicode characters that are encoded in UTF-8 format.

Characters are expressed in Golang by enclosing them in single quotes like this: ‘A’.

The default type for character values is rune. That means, if you don’t declare a type explicitly when declaring a variable with a character value, then Go will infer the type as rune -

1
var firstLetter = 'A' // Type inferred as `rune` (Default type for character values)

You can create a byte variable by explicitly specifying the type -

1
var lastLetter byte = 'Z'

Both byte and rune data types are essentially integers. For example, a byte variable with value ‘a’ is converted to the integer 97. Similarly, a rune variable with a unicode value ‘♥’ is converted to the corresponding unicode codepoint U+2665, where U+ means unicode and the numbers are hexadecimal, which is essentially an integer.

Operations on Numberic Types

Go provides several operators for performing operations on numeric types -

  • Arithmetic Operators: +, -, *, /, %
  • Comparison Operators: ==, !=, <, >, <=, >=
  • Bitwise Operators: &, |, ^, <<, >>
  • Increment and Decrement Operators: ++, --
  • Assignment Operators: +=, -=, *=, /=, %=, <<=, >>=, &=, |=, ^=

Here is an example demonstrating some of the above operators -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main
import (
"fmt"
"math"
)
func main() {
var a, b = 4, 5
var res1 = (a + b) * (a + b)/2 // Arithmetic operations

a++ // Increment a by 1

b += 10 // Increment b by 10

var res2 = a ^ b // Bitwise XOR

var r = 3.5
var res3 = math.Pi * r * r // Operations on floating-point type

fmt.Printf("res1 : %v, res2 : %v, res3 : %v\n", res1, res2, res3)
}
1
2
# Output
res1 : 40, res2 : 10, res3 : 38.48451000647496

Operations on Boolean Types

You can use the following operators on boolean types -

  • Logical Operators:
    • && (logical conjunction, “and”)
    • || (logical disjunction, “or”)
    • ! (logical negation)
  • Equality and Inequality:
    • == (“equals”)
    • != (“not equals”)

The operators && and || follow short-circuiting rules. That means, in the expression E1 && E2, if E1 evaluates to false then E2 won’t be evaluated. Similarly, in the expression E1 || E2, if E1evaluates to true then E2 won’t be evaluated.

Here is an example of Boolean types-

1
2
3
4
5
6
7
8
9
10
11
12
13
package main
import "fmt"

func main() {
var truth = 3 <= 5
var falsehood = 10 != 10

// Short Circuiting
var res1 = 10 > 20 && 5 == 5 // Second operand is not evaluated since first evaluates to false
var res2 = 2*2 == 4 || 10%3 == 0 // Second operand is not evaluated since first evaluates to true

fmt.Println(truth, falsehood, res1, res2)
}
1
2
# Output
true false false true

Operations on Complex Types

You can perform arithmetic operations like addition, subtraction, multiplication, and division on complex numbers -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main
import "fmt"

func main() {
var a = 3 + 5i
var b = 2 + 4i

var res1 = a + b
var res2 = a - b
var res3 = a * b
var res4 = a / b

fmt.Println(res1, res2, res3, res4)
}
1
2
# Output
(5+9i) (1+1i) (-14+22i) (1.3-0.1i)

Strings

In Go, a string is a sequence of bytes.

Strings in Golang are declared either using double quotes as in "Hello World" or back ticks as in `Hello World`.

1
2
3
4
5
6
// Normal String (Can not contain newlines, and can have escape characters like `\n`, `\t` etc)
var name = "Steve Jobs"

// Raw String (Can span multiple lines. Escape characters are not interpreted)
var bio = `Steve Jobs was an American entrepreneur and inventor.
He was the CEO and co-founder of Apple Inc.`

Double-quoted strings cannot contain newlines but they can have escape characters like \n, \t etc. In double-quoted strings, a \n character is replaced with a newline, and a \t character is replaced with a tab space, and so on.

Strings enclosed within back ticks are raw strings. They can span multiple lines. Moreover, Escape characters don’t have any special meaning in raw strings.

1
2
3
4
5
6
7
8
9
10
11
12
package main
import "fmt"

func main() {
var website = "\thttps://www.callicoder.com\t\n"

var siteDescription = `\t\tCalliCoder is a programming blog where you can find
practical guides and tutorials on programming languages,
web development, and desktop app development.\t\n`

fmt.Println(website, siteDescription)
}
1
2
3
4
5
# Output
https://www.callicoder.com
\t\tCalliCoder is a programming blog where you can find
practical guides and tutorials on programming languages,
web development, and desktop app development.\t\n

There is quite a lot about operations in String, which will be covered later, but not in this section.

Type Conversion

The expression T(v) converts the value v to the type T.

Some numeric conversions:

1
2
3
var i int = 42
var f float64 = float64(i)
var u uint = uint(f)

Or, put more simply:

1
2
3
i := 42
f := float64(i)
u := uint(f)

Unlike in C, in Go assignment between items of different type requires an explicit conversion.

Additionally, Golang has a strong type system. It doesn’t allow you to mix numeric types in an expression. For example, You cannot add an int variable to a float64 variable or even an int variable to an int64 variable. You cannot even perform an assignment between mixed types -

1
2
3
4
5
6
var a int64 = 4
var b int = a // Compiler Error (Cannot use a (type in64) as type int in assignment)

var c int = 500

var result = a + c // Compiler Error (Invalid Operation: mismatched types int64 and int)

Unlike other statically typed languages like C, C++, and Java, Go doesn’t provide any implicit type conversion.

Constants

In Golang, we use the term constant to represent fixed (unchanging) values such as 5, 1.34, true, "Hello" etc.

Constants can be character, string, boolean, or numeric values. In fact, all the literals in Golang, be it integer literals like 5, 1000, or floating-point literals like 4.76, 1.89, or boolean literals like true, false, or string literals like "Hello", "John" are constants (cannot be modified). This means that self-made types cannot be declared as constants.

Some examples are:

Constants Examples
integer constants 1000, 67413
floating-point constants 4.56, 128.372
boolean constants true, false
rune constants ‘C’, ‘ä’
complex constants 2.7i, 3 + 5i
string constants “Hello”, “Rajeev”

Declaring a Constant

Literals are basically constants without a name. To declare a constant and give it a name, you can use the const keyword like so -

1
const sunRisesInTheEast = true

You can also specify a type in the declaration like this -

1
2
const a int = 1234
const b string = "Hi"

Multiple declarations in a single statement is also possible -

1
2
3
4
5
6
const country, code = "India", 91

const (
employeeId string = "E101"
salary float64 = 50000.0
)

Constants declarations are very similar to variable declaratios, except that constans cannot be declared using the := syntax, but with =.

Typed and Untyped Constants

This basically has the same mechanism of a typed and an untyped variable. So it is not covered here again. Even the default types used for type inference are the same.

If you really would like to see the details on constants, please visit https://www.callicoder.com/golang-typed-untyped-constants/ for more details.

Control Flow Statements

Go has only one looping construct, the for loop.

For Loop

The basic for loop has three components separated by semicolons (same as Java):

  • the init statement: executed before the first iteration
  • the condition expression: evaluated before every iteration
  • the post statement: executed at the end of every iteration

The init statement will often be a short variable declaration, and the variables declared there are visible only in the scope of the for statement. Also, alike Java, the init and post statements are optional. For example:

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func main() {
sum := 1
for ; sum < 1000; {
sum += sum
}
fmt.Println(sum)
}

Unlike other languages like C, Java, or JavaScript there are no parentheses surrounding the three components of the for statement and the braces { } for code block are always required.

“While” Loop

There is no actual while loop in Go. However, since the init and post statements are optional, the for loop abbreviated could be used as a while loop:

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func main() {
sum := 1
for sum < 1000 { // equivalent as while(sum < 1000) in Java
sum += sum
}
fmt.Println(sum)
}

If you omit the loop condition it loops forever, so an infinite loop is compactly expressed.

1
2
3
4
5
6
package main

func main() {
for { // same as while(true) in Java
}
}

Break/Continue in For Loop

You can use break statement to break out of a loop before its normal termination. Here is an example -

1
2
3
4
5
6
7
8
9
10
11
package main
import "fmt"

func main() {
for num := 1; num <= 100; num++ {
if num%3 == 0 && num%5 == 0 {
fmt.Printf("First positive number divisible by both 3 and 5 is %d\n", num)
break // same as other languages
}
}
}

Similarly, the continue statement is used to stop running the loop body midway and continue to the next iteration of the loop.

1
2
3
4
5
6
7
8
9
10
11
package main
import "fmt"

func main() {
for num := 1; num <= 10; num++ {
if num%2 == 0 {
continue // same as other languages
}
fmt.Printf("%d ", num)
}
}

if/else if/else Control Flow

Go’s if/else if/else statements are like its for loops; the expression need not be surrounded by parentheses ( ) but the braces { } are required.

1
2
3
4
5
6
7
8
9
10
11
12
package main
import "fmt"

func main() {
var x = 25 // inferred as int

if x % 5 == 0 { // you could surrond it with ( ), but it is optional in Go
fmt.Printf("%d is a multiple of 5\n", x)
}else{
fmt.Printf("%d is a not multiple of 5\n", x)
}
}

The else if is similar to if, except that there can be multiple of them:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main
import "fmt"

func main() {
var BMI = 21.0
if BMI < 18.5 {
fmt.Println("You are underweight");
} else if BMI >= 18.5 && BMI < 25.0 {
fmt.Println("Your weight is normal");
} else if BMI >= 25.0 && BMI < 30.0 {
fmt.Println("You're overweight")
} else {
fmt.Println("You're obese")
}
}

if/else if Short Declaration

Like for, the if/else if statement can start with a short statement to execute before the condition.

Variables declared by the statement are only in scope until the end of the entire if/else if/else loop. For example:

1
2
3
4
5
6
7
8
9
10
func IfWithShortStatement (input int)(){
if upper,lower:= 100, 0; input < upper && upper > lower {
fmt.Printf("%d is within %d and %d, exclusive\n",input,lower,upper)
}else if upper,lower = 200,100; input < upper && upper > lower {
fmt.Printf("%d is within %d and %d, exclusive\n",input,lower,upper)
}else {
fmt.Printf("current number is: high %d, low %d\n",upper,lower) // upper and lower is still available here
}
// here, both variables will no longer be available
}

Note that, if you’re using a short statement, then you can’t use parentheses. So the following code will generate a syntax error -

1
2
3
4
// You can't use parentheses when `if` contains a short statement
if (n := 15; n%2 == 0) { // Syntax Error

}

Switch Statement

A switch statement is a shorter way to write a sequence of if - else statements. It runs the first case whose value is equal to the condition expression.

Go’s switch is like the one in C, C++, Java, JavaScript, and PHP, except that Go only runs the selected case, not all the cases that follow. In effect, the break statement that is needed at the end of each case in those languages is provided automatically in Go.

Another important difference is that Go’s switch cases need not be constants, and the values involved need not be integers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
"fmt"
"runtime"
)

func main() {
fmt.Print("Go runs on ")
switch os := runtime.GOOS; os { // so the variable os is the switch condition
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
case "windows":
fmt.Println("Windows")
default:
// freebsd, openbsd,
// plan9
fmt.Printf("%s.\n", os)
}
}

Switch Without A Condition

Switch without a condition is the same as switch true.

This construct can be a clean way to write long if-then-else chains.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"fmt"
"time"
)

func main() {
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("Good morning!")
case t.Hour() < 17:
fmt.Println("Good afternoon.")
default:
fmt.Println("Good evening.")
}
}

Defer

A defer statement defers the execution of a function until the surrounding function returns (it is like the finally cause in a try block in Java)

The deferred call’s arguments are evaluated immediately, but the function call is not executed until the surrounding function returns.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// Go program to illustrate the 
// concept of the defer statement
package main

import "fmt"

// Functions
func mul(a1, a2 int) int {

res := a1 * a2
fmt.Println("Result: ", res)
return 0
}

func show() {
fmt.Println("Hello!, GeeksforGeeks")
}

// Main function
func main() {

// Calling mul() function
// Here mul function behaves
// like a normal function
mul(23, 45)

// Calling mul()function
// Using defer keyword
// Here the mul() function
// is defer function
defer mul(23, 56)

// Calling show() function twice
show()
show()
}
1
2
3
4
5
6
Output:

Result: 1035
Hello!, GeeksforGeeks
Hello!, GeeksforGeeks
Result: 1288

Stacking Defers

Deferred function calls are pushed onto a stack. When a function returns, its** deferred calls are executed in last-in-first-out order**.

For example, 9 will be the first result that you will see:

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func main() {
fmt.Println("counting")

for i := 0; i < 10; i++ {
defer fmt.Println(i)
}

fmt.Println("done")
}

Pointers

(additional reference: https://www.callicoder.com/golang-pointers/a)

Go has pointers. A pointer holds the memory address of a value.

1
2
// A pointer of type T
var p *T

The type *T is a pointer to a T value. Its zero value is nil. For example:

1
2
// a pointer of type int
var p *int

The above pointer can only store the memory address of int variables.

Initializing a Pointer

To initialize a pointer with the memory address of another variable you can use &. The address of a variable can be retrieved using the & operator:

1
2
i := 42
var p *int = &i

Just like the other types, pointers can also be inferred by Go:

1
2
i := 42
p := &i // var p = &i, both are equivalent as above

Dereferening a Pointer

The * operator on a variable (not a type) denotes the pointer’s underlying value by going to that address and fetching its value.

1
2
fmt.Println(*p) // read i through the pointer p
*p = 21 // set i through the pointer p

This is known as “dereferencing” or “indirecting”.

However, you if use a pointer on another pointer, then:

1
2
3
p2 = &p1	// where p1 is already a pointer pointing to a variable
fmt.Println(*p2) // retrieves the address that p1 stores
fmt.Prinln(**p2) // retrieves the value that p1's address points to

You can not only access the value of the pointed variable using * operator, but you can change it as well. The following example sets the value stored in the variable a through the pointer p -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main
import "fmt"

func main() {
var a = 1000
var p = &a

fmt.Println("a (before) = ", a)

// Changing the value stored in the pointed variable through the pointer
*p = 2000

fmt.Println("a (after) = ", a)
}
1
2
3
# Output
a (before) = 1000
a (after) = 2000

Creating a Pointer using the built-in new() function

You can also create a pointer using the built-in new() function. The new() function takes a type as an argument, allocates enough memory to accommodate a value of that type, initializes it with the zero value and returns a pointer to it.

Here is an example -

1
2
3
4
5
6
7
8
9
10
package main

import "fmt"

func main() {
ptr := new(int) // Pointer to an `int` type, at this point *ptr is 0
*ptr = 100 // now *ptr is 100

fmt.Printf("Ptr = %#x, Ptr value = %d\n", ptr, *ptr)
}
1
2
# Output
Ptr = 0xc420014058, Ptr value = 100

However, unlike C, Go has no pointer arithmetic. For example, you cannot increment/decrement a pointer to move to the next/previous memory address. You cannot add or subtract an integer value to/from a pointer. You can also compare two pointers using relational operators <, > etc. Any such operation will result in a compile time error:

1
2
3
4
5
6
7
8
package main

func main() {
var x = 67
var p = &x

var p1 = p + 1 // Compiler Error: invalid operation
}

You can, however, compare two pointers of the same type for equality using == operator. This basically compares the address stored, if they have the same address, then it returns true, otherwise false.

1
2
3
4
5
6
7
8
9
10
11
12
package main
import "fmt"

func main() {
var a = 75
var p1 = &a
var p2 = &a

if p1 == p2 {
fmt.Println("Both pointers p1 and p2 point to the same variable.")
}
}

Structs

(additional reference: https://www.callicoder.com/golang-structs/)

A struct is a user-defined type that contains a collection of named fields/properties. It is used to group related data together to form a single unit.

If you’re coming from an object-oriented background, you can think of a struct as a lightweight class that supports composition but NOT inheritance.

Defining a Struct Type

You can define a new struct type like this -

1
2
3
4
5
type Person struct {	// note this is a curly brace
FirstName string // you could also use: FirstName,Lastname string
LastName string
Age int
}

The type keyword introduces a new type. It is followed by the name of the type (Person) and the keyword struct to indicate that we’re defining a struct (another option would be interface, which will be covered later). The struct contains a list of fields inside the curly braces { }. Each field has a name and a type.

Instantiating a Struct Type

Just like other data types, you can declare a variable of a struct type like this -

1
2
// Declares a variable of type 'Person'
var p Person // All the struct fields are initialized with their zero value

The above code creates a variable of type Person that is by default set to zero. For a struct, zero means all the fields are set to their corresponding zero value. So the fields FirstName and LastName are set to "", and Age is set to 0.

You can initialize a variable of a struct type by passing in values required by that struct with { } -

1
2
// Initialize a struct by supplying the value of all the struct fields.
var p = Person{"Rajeev", "Singh", 26}

Note that you need to pass the field values in the same order in which they are declared in the struct (if you don’t specify the variable names). Also, you can’t initialize only a subset of fields with the above syntax -

1
var p = Person{"Rajeev"} // Compiler Error: too few values in struct initializer

However, Go also supports the name: value syntax for initializing a struct, so that the order of fields becomes irrelevant.

1
var p = Person{LastName: "Singh", Age: 25, FirstName: "Rajeev"}

The name: value syntax allows you to initialize only a subset of fields. All the uninitialized fields are set to their corresponding zero value:

1
var p = Person{FirstName: "Alien"} // LastName: "", Age: 0

Accessing fields of a Struct

This is same as accessing the field of a Java object - you use the . operator:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import (
"fmt"
)

type Car struct {
Name, Model, Color string
WeightInKg float64
}

func main() {
c := Car{
Name: "Ferrari",
Model: "GTC4",
Color: "Red",
WeightInKg: 1920,
}

// Accessing struct fields using the dot operator
fmt.Println("Car Name: ", c.Name)
fmt.Println("Car Color: ", c.Color)

// Assigning a new value to a struct field
c.Color = "Black"
fmt.Println("Car: ", c)
}

Pointer to Structs

Similar to those built-in structs, you can get a pointer to a struct using the & operator on it -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import (
"fmt"
)

type Student struct {
RollNumber int
Name string
}

func main() {
// instance of student struct type
s := Student{11, "Jack"}

// Pointer to the student struct
ps := &s
fmt.Println(ps)

// Accessing struct fields via pointer
fmt.Println((*ps).Name)
fmt.Println(ps.Name) // Same as above: No need to explicitly dereference the pointer

ps.RollNumber = 31
fmt.Println(ps)
}

Notice that. to access the field X of a struct when we have the struct pointer p we could write (*p).X. However, that notation is cumbersome, so the language permits us instead to write just p.X, without the explicit dereference (it only dereferences for one level, so nested pointers still need *).

Creating a struct using the built-in new() function

You can also use the built-in new() function to create an instance of a struct, but alike its previous usage, it returns to you the address of that struct. The new() function allocates enough memory to fit all the struct fields, sets each of them to their zero value and returns a pointer to the newly allocated struct -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

type Employee struct {
Id int
Name string
}

func main() {
// You can also get a pointer to a struct using the built-in new() function
// It allocates enough memory to fit a value of the given struct type, and returns a pointer to it
pEmp := new(Employee)

pEmp.Id = 1000
pEmp.Name = "Sachin"

fmt.Println(pEmp)
}

Exported and Unexported Fields in a Struct

Any struct type that starts with a capital letter is exported and accessible from outside packages. Similarly, any struct field that starts with a capital letter is exported (visible outside package).

On the contrary, all the names starting with a small letter are visible only inside the same package.

Let’s see an example. Consider the following package hierarchy of our Go program -

1
2
3
4
5
6
7
8
package model

type Customer struct { // exported struct type
Id int // exported field
Name string // exported field
addr address // unexported field (only accessible inside package `model`)
married bool // unexported field (only accessible inside package `model`)
}

Then, to initialize the Customer struct, you would need to explicitly use the name: value declaration:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import (
"fmt"
"example/model"
)

func main() {
c := model.Customer{
Id: 1,
Name: "Rajeev Singh",
}

c.married = true // Error: can not refer to unexported field or method

a := model.address{} // Error: can not refer to unexported name

fmt.Println("Programmer = ", c);
}

In the above example, if you use c := model.Customer{1,"Rajjev Singh"}, it will give you Compile Error.

Structs and Short Declarations

Now, since pointers are used to store a reference to a piece of data, (short) variable declarations without specifying a pointer actually make a new copy of the variable:

1
2
3
4
5
// the below three all have the same value but different memory address

a1 := 10
a2 := a1
var a3 int = a1

This is the same with using a struct:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import "fmt"

type Point struct {
X float64
Y float64
}

func main() {
// Structs are value types.
p1 := Point{10, 20}
p2 := p1 // A COPY of the struct `p1` is assigned to `p2`
fmt.Println("p1 = ", p1)
fmt.Println("p2 = ", p2)

p2.X = 15 // does not change p1
fmt.Println("\nAfter modifying p2:")
fmt.Println("p1 = ", p1)
fmt.Println("p2 = ", p2)
}

Equality ==

This means that if you use == between two structs, it compares the actual value of the fields in that struct. This is diffrent from Java, where == would be used to compare the memory address (you can compare addresses if you are comparing two pointers).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import "fmt"

type Point struct {
X float64
Y float64
}

func main() {
// Two structs are equal if all their corresponding fields are equal.
p1 := Point{3.4, 5.2}
p2 := Point{3.4, 5.2}

if p1 == p2 {
fmt.Println("Point p1 and p2 are equal.")
} else {
fmt.Println("Point p1 and p2 are not equal.")
}
}
1
2
# Output
Point p1 and p2 are equal.

Arrays

(additional reference: https://www.callicoder.com/golang-arrays/)

An array is a fixed-size collection of elements of the same type. The elements of the array are stored sequentially and can be accessed using their index. (Similar to other programming languages)

Declaring an Array

The type [n]T is an array of n values of type T.

The expression:

1
var a [10]int

declares a variable a as an array of ten integers (int).

An array’s length is part of its type, so arrays cannot be resized. This seems limiting, but don’t worry; Go provides a convenient way of working with arrays.

Now let’s see a complete example -

1
2
3
4
5
6
7
8
9
10
11
12
13
package main
import "fmt"

func main() {
var x [5]int // An array of 5 integers
fmt.Println(x)

var y [8]string // An array of 8 strings
fmt.Println(y)

var z [3]complex128 // An array of 3 complex numbers
fmt.Println(z)
}
1
2
3
4
# Output
[0 0 0 0 0]
[ ]
[(0+0i) (0+0i) (0+0i)]

By default, all the array elements are initialized with the zero value of the corresponding array type.

You can declare and initialize an array at the same time like this (similar to Java) -

1
2
// Declaring and initializing an array at the same time
var a = [5]int{2, 4, 6, 8, 10}

You can also let Go infer the size of an array at initialization time:

1
2
arr := []int{1,2,3,4,5}		// infers the size to 5, uses a SLICE, rather an array
fmt.Println(len(arr))

Modifying an Array

This is the same in any other language, that elements in an array can be modified with an index:

1
arr[1] = 5	// changes the second element to 5

Slices

An array has a fixed size. A slice, on the other hand, is a dynamically-sized, flexible view into the elements of an array. In practice, slices are much more common than arrays. (Internally, a Slice is just a reference to an existing array, if it is constructed from an array.)

Declaring a Slice

There are two (actually four) ways to declare a slice. You can either create a brand new slice, or create a slice based on an existing array.

Creating from an Existing Array

A slice is formed by specifying two indices, a low and high bound, separated by a colon:

1
arr[low : high]	// where arr is an array

This selects a half-open range which includes the first element, but excludes the last one (alike Python).

The following expression creates a slice which includes elements 1 through 3 of a:

1
a[1:4] 	// takes elements 1 to 3, inclusive

For example:

1
2
3
4
5
6
7
8
9
10
package main

import "fmt"

func main() {
primes := [6]int{2, 3, 5, 7, 11, 13} // an existing array

var s []int = primes[1:4] // a slice from that array
fmt.Println(s)
}

However, since this slice is constructed from an array, under the hood it actually stores the references to the array. This means that changing the elements of a slice modifies the corresponding elements of its underlying array. Additionlly, other slices that share the same underlying array will see those changes.

Creating a Brand New Slice

A slice literal is like an array literal without the length.

The type []T can also be seen as a slice with elements of type T. Note the only difference is that you do not specify the size/length, which would be an array creation.

For example:

1
2
// Creating a slice using a slice literal
var s = []int{3, 5, 7, 9, 11, 13, 17} // would be appendable

Under the hood, when you create a slice using a slice literal, it first creates an array and then returns a slice reference to it.

Slicing Defaults

When slicing, you may omit the high or low bounds to use their defaults instead:

  • zero for the low bound
  • length of the slice for the high bound

For example, if you have an array that looks like

1
var a = [10]int {1,2,3,4....}	// some numbers omitted

these slice expressions are equivalent:

1
2
3
4
a[0:10]
a[:10]
a[0:]
a[:]

Creating a Slice from another Slice

This is a third way to create a slice, however, there are some details that is different.

length and a capacity

The length of a slice is the number of elements it contains. The capacity of a slice is the number of elements in the underlying array, counting from the first element in the slice.

The length and capacity of a slice s can be obtained using the expressions len(s) and cap(s), where s is a slice.

Now, if you created a slice from another slice, the range of elements you can retrieve is bounded by the capacity of that parent slice.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import "fmt"

func main() {
s := []int{2, 3, 5, 7, 11, 13}
printSlice(s)

// Slice the slice to give it zero length. This does not change capacity
s = s[:0]
printSlice(s)

// Extend its length.
s = s[:4]
printSlice(s)

// Drop its first two values. This changes the capacity to 4
s = s[2:]
printSlice(s)

s = s[:] // cannot go s[:5], since the capacity becomes 4
printSlice(s)
}

func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

Creating a Slice with make

Slices can be created with the built-in make function; this is how you create dynamically-sized arrays.

The make function allocates a zeroed array and returns a slice that refers to that array:

1
a := make([]int, 5)  // len(a)=5, filled with zero-value of 0

To specify a capacity, pass a third argument to make:

1
2
3
4
b := make([]int, 0, 5) // len(b)=0, cap(b)=5

b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:] // len(b)=4, cap(b)=4

Functions for Slice

(addtional reference: https://www.callicoder.com/golang-slices/#slice-functions)

Appending to a slice

The append() function appends new elements at the end of a given slice. Following is the signature of append function.

1
func append(s []T, x ...T) []T

It takes a slice and a variable number of arguments x …T. It then returns a new slice containing all the elements from the given slice as well as the new elements.

If the given slice doesn’t have sufficient capacity to accommodate new elements then a new underlying array is allocated with bigger capacity. All the elements from the underlying array of the existing slice are copied to this new array, and then the new elements are appended.

However, if the slice has enough capacity to accommodate new elements, then the append() function re-uses its underlying array and appends new elements to the same array, it will also overwrite the existing element if it was there.

Copying a Slice

The copy() function copies elements from one slice to another (by creating another array and then copying the value over). Its signature looks like this -

1
func copy(dst, src []T) int

It takes two slices - a destination slice, and a source slice. It then copies elements from the source to the destination and returns the number of elements that are copied.

The number of elements copied will be the minimum of len(src) and len(dst), not cap.

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
package main
import "fmt"

func main() {
src := []string{"Sublime", "VSCode", "IntelliJ", "Eclipse"}
dest := make([]string, 2)

numElementsCopied := copy(dest, src) // changing dest here will not affect src

fmt.Println("src = ", src)
fmt.Println("dest = ", dest)
fmt.Println("Number of elements copied from src to dest = ", numElementsCopied)
}

Iterating over a Slice

You can iterate over a slice in the same way you iterate over an array. Following are two ways of iterating over a slice:

  • the conventional for loop, which will not be demonstrated here as it is trivial
  • using the range with an enhanced for loop

Using range

The following shows an example of using a range on a slice. However, it is the same for an arary:

1
2
3
4
5
6
7
8
9
10
package main
import "fmt"

func main() {
primeNumbers := []int{2, 3, 5, 7, 11, 13, 17, 19, 23, 29}

for index, number := range primeNumbers { // notice that it returns both index and element
fmt.Printf("PrimeNumber(%d) = %d\n", index+1, number)
}
}

You can skip the index or value by assigning to _.

1
2
for i, _ := range pow
for _, value := range pow

If you only want the index, you can omit the second variable.

1
for i := range pow	// same as for i,_ := range pow

Maps

(additional reference: https://www.callicoder.com/golang-maps/)

A map is an unordered collection of key-value pairs. It maps keys to values. The keys are unique within a map while the values may not be.

Declaring a Map

(additional reference: https://www.callicoder.com/golang-maps/)

A map is declared using the following syntax -

1
var m map[KeyType]ValueType

For example, Here is how you can declare a map of string keys to int values -

1
var m map[string]int

The zero value of a map is nil (like null in other languages). A nil map has no keys, nor can keys be added. So, any attempt to add keys to a nil map will result in a runtime error.

Let’s see an example -

1
2
3
4
5
6
7
8
9
10
11
12
13
package main
import "fmt"

func main() {
var m map[string]int
fmt.Println(m)
if m == nil {
fmt.Println("m is nil")
}

// Attempting to add keys to a nil map will result in a runtime error
// m["one hundred"] = 100
}
1
2
3
# Output
map[]
m is nil

If you uncomment the statement m["one hundred"] = 100, the program will generate the following error -

1
panic: assignment to entry in nil map

It is, therefore, necessary to initialize a map before adding items to it.

Initializing a Map

There are two ways to initialize a map. You can either use the make function, or you can use a map literal to initalize a map.

Initializing a map using the built-in make() function

You can initialize a map using the built-in make() function. You just need to pass the type of the map to the make() function as in the example below. The function will return an initialized and ready to use map -

1
2
// Initializing a map using the built-in make() function
var m = make(map[string]int)

Let’s see a complete example -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main
import "fmt"

func main() {
var m = make(map[string]int)

fmt.Println(m)

if m == nil {
fmt.Println("m is nil")
} else {
fmt.Println("m is not nil")
}

// make() function returns an initialized and ready to use map.
// Since it is initialized, you can add new keys to it.
m["one hundred"] = 100
fmt.Println(m)
}
1
2
3
4
# Output
map[]
m is not nil
map[one hundred:100]

Initializing a map using a map literal

A map literal is a very convenient way to initialize a map with some data. An empty map using a map literal by leaving the curly braces empty -

1
2
// Initialize an empty map
var m = map[string]int{}

The above statement is functionally identical to using the make() function.

To initialize with some values, you just need to pass the key-value pairs separated by colon inside curly braces like this:

1
2
3
4
5
var m = map[string]int{
"one": 1,
"two": 2,
"three": 3,
}

Adding items (key-value pairs) to a map

You can add new items to an initialized map using the following syntax -

1
m[key] = value

The following example initializes a map using the make() function and adds some new items to it -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main
import "fmt"

func main() {
// Initializing a map
var tinderMatch = make(map[string]string)

// Adding keys to a map
tinderMatch["Rajeev"] = "Angelina" // Assigns the value "Angelina" to the key "Rajeev"
tinderMatch["James"] = "Sophia"
tinderMatch["David"] = "Emma"

fmt.Println(tinderMatch)

/*
Adding a key that already exists will simply override
the existing key with the new value
*/
tinderMatch["Rajeev"] = "Jennifer"
fmt.Println(tinderMatch)
}
1
2
3
# Output
map[Rajeev:Angelina James:Sophia David:Emma]
map[Rajeev:Jennifer James:Sophia David:Emma]

If you try to add a key that already exists in the map, then it will simply be overridden by the new value.

Retrieving the value associated with a given key in a map

You can retrieve the value assigned to a key in a map using the syntax m[key]. If the key exists in the map, you’ll get the assigned value. Otherwise, you’ll get the zero value of the map’s value type.

Let’s check out an example to understand this -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main
import "fmt"

func main() {
var personMobileNo = map[string]string{
"John": "+33-8273658526",
"Steve": "+1-8579822345",
"David": "+44-9462834443",
}

var mobileNo = personMobileNo["Steve"]
fmt.Println("Steve's Mobile No : ", mobileNo)

// If a key doesn't exist in the map, we get the zero value of the value type
mobileNo = personMobileNo["Jack"] // string has a zero value of ""
fmt.Println("Jack's Mobile No : ", mobileNo)
}
1
2
3
# Output
Steve's Mobile No : +1-8579822345
Jack's Mobile No :

In the above example, since the key "Jack" doesn’t exist in the map, we get the zero value of the map’s value type. Since the map’s value type is string, we get " ".

Unlike other languages, we do not get a runtime error in Golang if the key doesn’t exist in the map.

But what if you want to check for the existence of a key?

Checking if a key exists in a map

When you retrieve the value assigned to a given key using the syntax map[key], it returns an additional boolean value as well which is true if the key exists in the map, and false if it doesn’t exist.

So you can check for the existence of a key in a map by using the following two-value assignment -

1
value, ok := m[key]

The boolean variable ok will be true if the key exists, and false otherwise.

Consider the following map for example. It maps employeeIds to names -

1
2
3
4
5
var employees = map[int]string{
1001: "Rajeev",
1002: "Sachin",
1003: "James",
}

Accessing the key 1001 will return “Rajeev” and true, since the key 1001 exists in the map -

1
name, ok := employees[1001]  // "Rajeev", true

However, If you try to access a key that doesn’t exist, then the map will return an empty string "" (zero value of strings), and false -

1
name, ok := employees[1010]  // "", false

Deleting a key from a map

You can delete a key from a map using the built-in delete() function. The syntax looks like this -

1
2
// Delete the `key` from the `map`
delete(map, key)

The delete() function doesn’t return any value. Also, it doesn’t do anything if the key doesn’t exist in the map.

Here is a complete example -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import "fmt"

func main() {
var fileExtensions = map[string]string{
"Python": ".py",
"C++": ".cpp",
"Java": ".java",
"Golang": ".go",
"Kotlin": ".kt",
}

fmt.Println(fileExtensions)

delete(fileExtensions, "Kotlin")

// delete function doesn't do anything if the key doesn't exist
delete(fileExtensions, "Javascript")

fmt.Println(fileExtensions)
}
1
2
3
# Output
map[Python:.py C++:.cpp Java:.java Golang:.go Kotlin:.kt]
map[Python:.py C++:.cpp Java:.java Golang:.go]

Maps are reference types

Maps are reference types. When you assign a map to a new variable, they both refer to the same underlying data structure. Therefore changes done by one variable will be visible to the other -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main
import "fmt"

func main() {
var m1 = map[string]int{
"one": 1,
"two": 2,
"three": 3,
"four": 4,
"five": 5,
}

var m2 = m1
fmt.Println("m1 = ", m1)
fmt.Println("m2 = ", m2)

m2["ten"] = 10
fmt.Println("\nm1 = ", m1)
fmt.Println("m2 = ", m2)
}
1
2
3
4
5
6
# Output
m1 = map[one:1 two:2 three:3 four:4 five:5]
m2 = map[one:1 two:2 three:3 four:4 five:5]

m1 = map[one:1 two:2 three:3 four:4 five:5 ten:10]
m2 = map[one:1 two:2 three:3 four:4 five:5 ten:10]

The same concept applies when you pass a map to a function. Any changes done to the map inside the function is also visible to the caller.

Iterating over a map

You can iterate over a map using range form of the for loop. It gives you the key, value pair in every iteration -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main
import "fmt"

func main() {
var personAge = map[string]int{
"Rajeev": 25,
"James": 32,
"Sarah": 29,
}

for name, age := range personAge {
fmt.Println(name, age)
}

}
1
2
3
4
# Output
James 32
Sarah 29
Rajeev 25

Note that, A map is an unordered collection and therefore the iteration order of a map is not guaranteed to be the same every time you iterate over it.

Function Values

Functions are values too. They can be passed around just like other values.

Function values may be used as function arguments and return values.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
"fmt"
"math"
)

func compute(fn func(float64, float64) float64) float64 {
return fn(3, 4)
}

func main() {
hypot := func(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
fmt.Println(hypot(5, 12))

fmt.Println(compute(hypot))
fmt.Println(compute(math.Pow))
}

Function closures

Go functions may be closures. A closure is a returned function value that references variables from outside its body. The function may access and assign to the referenced variables; in this sense the function is “bound” to the variables.

For example, the adder function returns a closure. Each closure is bound to its own sum variable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
"fmt"
)

func adder() (result func(int) int) {
sum := 0
result = func(x int) int {
sum += x
return sum
}
return result
}

func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}

Methods

(additional reference: https://www.callicoder.com/golang-methods-tutorial/)

Technically, Go is not an object-oriented programming language. It doesn’t have classes, objects, and inheritance.

However, Go has types. And, you can define methods on types. This allows for an object-oriented style of programming in Go.

A method is nothing but a function with a special receiver argument.

The receiver argument has a name and a type. It appears between the func keyword and the method name -

1
2
func (receiver Type) MethodName(parameterList) (returnTypes) {
}

The receiver can be either a struct type or a non-struct type.

Let’s see an example to understand how to define a method on a type and how to invoke such a method -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
"fmt"
)

// Struct type - `Point`
type Point struct {
X, Y float64
}

// Method with receiver `Point`
func (p Point) IsAbove(y float64) bool {
return p.Y > y
}

func main() {
p := Point{2.0, 4.0}

fmt.Println("Point : ", p)

fmt.Println("Is Point p located above the line y = 1.0 ? : ", p.IsAbove(1))
}

Methods with Pointer receivers

All the examples that we saw in the previous sections had a value receiver.

With a value receiver, the method operates on a copy of the value passed to it. Therefore, any modifications done to the receiver inside the method is not visible to the caller.

Go also allows you to define a method with a pointer receiver -

1
2
3
// Method with pointer receiver
func (receiver *Type) MethodName(parameterList) (returnTypes) {
}

Methods with pointer receivers can modify the value to which the receiver points. Such modifications are visible to the caller of the method as well -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import (
"fmt"
)

type Point struct {
X, Y float64
}

/*
Translates the current Point, at location (X,Y), by dx along the x axis and dy along the y axis
so that it now represents the point (X+dx,Y+dy).
*/
func (p *Point) Translate(dx, dy float64) {
p.X = p.X + dx
p.Y = p.Y + dy
}

func main() {
p := Point{3, 4}
fmt.Println("Point p = ", p)

p.Translate(7, 9)
fmt.Println("After Translate, p = ", p)
}
1
2
Point p =  {3 4}
After Translate, p = {10 13}

Function equivalent of a Method

Just like methods with value receivers, we can also write methods with pointer receivers as functions. The following example shows how to rewrite the previous example with functions -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import (
"fmt"
)

type Point struct {
X, Y float64
}

/*
Translates the current Point, at location (X,Y), by dx along the x axis and dy along the y axis
so that it now represents the point (X+dx,Y+dy).
*/
func TranslateFunc(p *Point, dx, dy float64) { // a function version of the above
p.X = p.X + dx
p.Y = p.Y + dy
}

func main() {
p := Point{3, 4}
fmt.Println("Point p = ", p)

TranslateFunc(&p, 7, 9)
fmt.Println("After Translate, p = ", p)
}

Methods and Pointer indirection

  1. Methods with Pointer receivers vs Functions with Pointer arguments

    A method with a pointer receiver can accept both a pointer and a value as the receiver argument. But, a function with a pointer argument can only accept a pointer -

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    package main

    import (
    "fmt"
    )

    type Point struct {
    X, Y float64
    }

    // Method with Pointer receiver, actually BOTH value and pointer will be accepted
    func (p *Point) Translate(dx, dy float64) {
    p.X = p.X + dx
    p.Y = p.Y + dy
    }

    // Function with Pointer argument, ONLY a pointer will be accepted
    func TranslateFunc(p *Point, dx, dy float64) {
    p.X = p.X + dx
    p.Y = p.Y + dy
    }

    func main() {
    p := Point{3, 4}
    ptr := &p
    fmt.Println("Point p = ", p)

    // Calling a Method with Pointer receiver
    p.Translate(2, 6) // Valid
    ptr.Translate(5, 10) // Valid

    // Calling a Function with a Pointer argument
    TranslateFunc(ptr, 20, 30) // Valid
    TranslateFunc(p, 20, 30) // Not Valid
    }

    Notice how both p.Translate() and ptr.Translate() calls are valid. Since Go knows that the method Translate() has a pointer receiver, It interprets the statement p.Translate() as (&p).Translate(). It’s a syntactic sugar provider by Go for convenience.

  2. Methods with Value receivers vs Functions with Value arguments

    A method with a value receiver can accept both a value and a pointer as the receiver argument. But, a function with a value argument can only accept a value -

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    package main

    import (
    "fmt"
    )

    // Struct type - `Point`
    type Point struct {
    X, Y float64
    }

    func (p Point) IsAbove(y float64) bool {
    return p.Y > y
    }

    func IsAboveFunc(p Point, y float64) bool {
    return p.Y > y
    }

    func main() {
    p := Point{0, 4}
    ptr := &p

    fmt.Println("Point p = ", p)

    // Calling a Method with Value receiver
    p.IsAbove(1) // Valid
    ptr.IsAbove(1) // Valid

    // Calling a Function with a Value argument
    IsAboveFunc(p, 1) // Valid
    IsAboveFunc(ptr, 1) // Not Valid
    }

    In the above example, both p.IsAbove() and ptr.IsAbove() statements are valid. Go knows that the method IsAbove() has a value receiver. So for convenience, It interprets the statement ptr.IsAbove() as (*ptr).IsAbove()

Method definition restrictions

Note that, For you to be able to define a method on a receiver, the receiver type must be defined in the same package.

Go doesn’t allow you to define a method on a receiver type that is defined in some other package (this includes built-in types such as int as well).

In all the previous examples, the structs and the methods were defined in the same package main. Therefore, they worked. However, if you try to define a method on a type defined in some other package, the compiler will complain.

Let’s see an example. Consider the following package hierarchy -

1
2
3
4
5
src/example
main
main.go
model
person.go

Here are the contents of person.go -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package model

type Person struct {
FirstName string
LastName string
Age int
}
Let’s try to define a method on the struct Person -

package main

import (
"example/model"
)

// ERROR: cannot define new methods on non-local types model.Person
func (p model.Person) getFullName() string {
return p.FirstName + " " + p.LastName
}

func main() {
}

Interface

(additional reference: https://www.callicoder.com/golang-interfaces/)

An interface in Go is a type defined using a set of method signatures. The interface defines the behavior for similar type of objects.

For example, Here is an interface that defines the behavior for Geometrical shapes:

1
2
3
4
5
// Go Interface - `Shape`
type Shape interface {
Area() float64
Perimeter() float64
}

An interface is declared using the type keyword, followed by the name of the interface and the keyword interface. Then, we specify a set of method signatures inside curly braces.

Implementing an interface in Go

To implement an interface, you just need to implement all the methods declared in the interface.

Go Interfaces are implemented implicitly

Unlike other languages like Java, you don’t need to explicitly specify that a type implements an interface using something like an implements keyword. You just implement all the methods declared in the interface and you’re done.

Here is one Struct types that implement the Shape interface:

1
2
3
4
5
// Go Interface - `Shape`
type Shape interface {
Area() float64
Perimeter() float64
}
1
2
3
4
5
6
7
8
9
10
11
12
// Struct type `Rectangle` - implements the `Shape` interface by implementing all its methods.
type Rectangle struct {
Length, Width float64
}

func (r Rectangle) Area() float64 {
return r.Length * r.Width
}

func (r Rectangle) Perimeter() float64 {
return 2 * (r.Length + r.Width)
}

Using an interface type with concrete values

An interface in itself is not that useful unless we use it with a concrete type that implements all its methods.

Let’s see how an interface can be used with concrete values.

  • An interface type can hold any value that implements all its methods
1
2
3
4
5
6
// this means you can do this
var s Shape = Circle{5.0}
... // some operations
s = Rectangle{4.0, 6.0}
... // some other operatoins
}
  • Using Interface types as arguments to functions
1
2
3
4
5
6
7
8
// Generic function to calculate the total area of multiple shapes of different types
func CalculateTotalArea(shapes ...Shape) float64 {
totalArea := 0.0
for _, s := range shapes {
totalArea += s.Area()
}
return totalArea
}
  • Using Interface types as fields
1
2
3
4
5
6
// Interface types can also be used as fields
type MyDrawing struct {
shapes []Shape
bgColor string
fgColor string
}

Interface values: How does an interface type work with concrete values?

Under the hood, an interface value can be thought of as a tuple consisting of a value and a concrete type:

1
2
// interface
(value, type)

Let’s see an example to understand more:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"fmt"
)

func main() {
var s Shape

s = Circle{5}
fmt.Printf("(%v, %T)\n", s, s)
fmt.Printf("Shape area = %v\n", s.Area())

s = Rectangle{4, 7}
fmt.Printf("(%v, %T)\n", s, s)
fmt.Printf("Shape area = %v\n", s.Area())
}
1
2
3
4
5
# Output
({5}, main.Circle)
Shape area = 78.53981633974483
({4 7}, main.Rectangle)
Shape area = 28

Checkout the output of the above program and notice how the variable s has information about the value as well as the type of the Shape that is assigned to it.

When we call a method on an interface value, a method of the same name on its underlying type (actual type) is executed.

For example, in the above program, when we call the method Area() on the variable s, it executes the Area() method of its underlying type/actual type.

What if you have a nil value for an Interface?

If the concrete value inside the interface itself is nil (but its type is not nil), the method will be called with a nil receiver.

In some languages this would trigger a null pointer exception, but in Go it is common to write methods that gracefully handle being called with a nil receiver (as with the method M in this example.)

Note that an interface value that holds a nil concrete value is itself non-nil.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package main

import "fmt"

type I interface {
M()
}

type T struct {
S string
}

func (t *T) M() {
if t == nil {
fmt.Println("<nil>")
return
}
fmt.Println(t.S)
}

func main() {
var i I

var t *T
i = t
describe(i)
i.M()

i = &T{"hello"}
describe(i)
i.M()
}

func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}

What if you have a nil type for an Interface?

You cannot create a variable with a value but not type. So the only case is that you have neither value nor type for an interface variable.

Calling a method on such a nil interface is a run-time error because there is no type inside the interface tuple to indicate which concrete method to call.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"

type I interface {
M()
}

func main() {
var i I
describe(i)
i.M()
}

func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}

The empty Interface

The interface type that specifies zero methods is known as the empty interface:

1
interface{}

An empty interface may hold values of any type. (Every type implements at least zero methods.)

Empty interfaces are used by code that handles values of unknown type. For example, fmt.Print takes any number of arguments of type interface{}.

To use this, you don’t need to explicitly creat an interface but just use it in place:

1
var i interface{}	// this variable will be of anyType

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

func main() {
var i interface{}
describe(i)

i = 42
describe(i)

i = "hello"
describe(i)
}

func describe(i interface{}) {
fmt.Printf("(%v, %T)\n", i, i)
}

Type Assertions

Type assertion provides access to and assess an interface value’s underlying concrete value.

1
t := i.(T)	// T is the type that I want to assert variable i has

This statement asserts that the interface value i holds the concrete type T and assigns the underlying T value to the variable t.

If i does not hold a T, the statement will trigger a panic.

To test whether an interface value holds a specific type, a type assertion can return two values: the underlying type value and a boolean value that reports whether the assertion succeeded.

1
t, ok := i.(T)

If i holds a T, then t will be the underlying value and ok will be true.

If not, ok will be false and t will be the zero value of type T, and no panic occurs.

Note the similarity between this syntax and that of reading from a map/array/slice.

Type switches

A type switch is a construct that permits several type assertions in series.

A type switch is like a regular switch statement, but the cases in a type switch specify types (not values), and those values are compared against the type of the value held by the given interface value.

1
2
3
4
5
6
7
8
switch v := i.(type) {
case T:
// here v has type T
case S:
// here v has type S
default:
// no match; here v has the same type as i
}

The declaration in a type switch has the same syntax as a type assertion i.(T), but the specific type T is replaced with the keyword type. (This only works if you use a switch statement.)

Common Interfaces

Stringer Interface

One of the most ubiquitous interfaces is Stringer defined by the fmt package. This could be understood similar to the String toString() method of a Java object, which would be invoked when println() is called.

1
2
3
type Stringer interface {
String() string
}

A Stringer is a type that can describe itself as a string. The fmt package (and many others) look for this interface to print values.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

type Person struct {
Name string
Age int
}

func (p Person) String() string { // the toString() for Person objects
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}

func main() {
a := Person{"Arthur Dent", 42}
z := Person{"Zaphod Beeblebrox", 9001}
fmt.Println(a, z)
}

Error Interface

Go programs express error state with error values.

The error type is a built-in interface similar to fmt.Stringer:

1
2
3
type error interface {
Error() string
}

(As with fmt.Stringer, the fmt package looks for the error interface when printing error values.)

Functions often return an error value, and calling code should handle errors by testing whether the error equals nil.

1
2
3
4
5
6
7
i, err := strconv.Atoi("42")
if err != nil {
fmt.Printf("couldn't convert number: %v\n", err) // if you implemented Error() string,
// printing err here would print that string
return
}
fmt.Println("Converted integer:", i)

A nil error denotes success; a non-nil error denotes failure.

Reader Interface

The io package specifies the io.Reader interface, which represents the read end of a stream of data.

The Go standard library contains many implementations of these interfaces, including files, network connections, compressors, ciphers, and others.

The io.Reader interface has a Read method:

1
func (T) Read(b []byte) (n int, err error)

Read populates the given byte slice with data and returns the number of bytes populated and an error value. It returns an io.EOF error when the stream ends.

The following example consumes output 8 bytes at a time:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"fmt"
"io"
"strings"
)

func main() {
r := strings.NewReader("Hello, Reader!") // a Reader class

b := make([]byte, 8)
for {
n, err := r.Read(b)
fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
fmt.Printf("b[:n] = %q\n", b[:n])
if err == io.EOF {
break
}
}
}

Go Routines

(additional reference: https://www.geeksforgeeks.org/goroutines-concurrency-in-golang/)

Go language provides a special feature known as a Goroutines. A Goroutine is a function or method which executes independently and simultaneously in connection with any other Goroutines present in your program. Or in other words, every concurrently executing activity in Go language is known as a Goroutines. You can consider a Goroutine like a light weighted thread. The cost of creating Goroutines is very small as compared to the thread.

Every program contains at least a single Goroutine and that Goroutine is known as the main Goroutine. All the Goroutines are working under the main Goroutines if the main Goroutine terminated, then all the goroutine present in the program also terminated. Goroutine always works in the background.

Creating a Goroutine

You can create your own Goroutine simply by using go keyword as a prefixing to the function or method call as shown in the below syntax:

1
2
3
4
5
6
7
func name(){
// statements
}

// using go keyword as the
// prefix of your function call
go name()

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Go program to illustrate 
// the concept of Goroutine
package main

import "fmt"

func display(str string) {
for w := 0; w < 6; w++ {
fmt.Println(str)
}
}

func main() {

// Calling Goroutine
go display("Welcome")

// Calling normal function
display("GeeksforGeeks")
}
1
2
3
4
5
6
7
8
# Output:

GeeksforGeeks
GeeksforGeeks
GeeksforGeeks
GeeksforGeeks
GeeksforGeeks
GeeksforGeeks

Notice that this does not work as expected. This is because that when you call go display("Welcome"), sometime is needed to initialize and execute the Goroutine. However, this time does not block the program from going forward. This means that before go display("Welcome") happens, the program executes the normal function call display("GeeksforGeeks") and finishes before go display("Welcome") happens. Once display("GeeksforGeeks") finished executing, the program finishes, so go display("Welcome") never actually executes.

To fix this, (there are many ways to fix this) you could give some time for that Goroutine to execute before the program exits:

1
2
3
4
5
6
7
func main() {	// though this technically does not have the effect we wanted
go display("Welcome")

display("GeeksforGeeks")

time.Sleep(2 * time.Second)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Output

GeeksforGeeks
GeeksforGeeks
GeeksforGeeks
GeeksforGeeks
GeeksforGeeks
GeeksforGeeks
Welcome
Welcome
Welcome
Welcome
Welcome
Welcome

To achieve the effect of concurrency that we wanted, we should:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main 

import (
"fmt"
"time"
)

func display(str string) {
for w := 0; w < 6; w++ {
time.Sleep(1 * time.Second) // this will give enough time for that Goroutine to go
fmt.Println(str)
}
}

func main() {

// Calling Goroutine
go display("Welcome")

// Calling normal function
display("GeeksforGeeks")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
# Output:

Welcome
GeeksforGeeks
GeeksforGeeks
Welcome
Welcome
GeeksforGeeks
GeeksforGeeks
Welcome
Welcome
GeeksforGeeks
GeeksforGeeks

Anonymous Goroutine

In Go language, you can also start Goroutine for an anonymous function or in other words, you can create an in-place Goroutine simply by using go keyword as a prefix of that function as shown in the below Syntax:

1
2
3
4
// Anonymous function call
go func (parameter_list){
// statement
}(arguments)

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Go program to illustrate how 
// to create an anonymous Goroutine
package main

import (
"fmt"
"time"
)

// Main function
func main() {

fmt.Println("Welcome!! to Main function")

// Creating Anonymous Goroutine
go func() {

fmt.Println("Welcome!! to GeeksforGeeks")
}()

time.Sleep(1 * time.Second) // give some time for that Goroutine to initialize and go
fmt.Println("GoodBye!! to Main function")
}
1
2
3
4
5
# Output:

Welcome!! to Main function
Welcome!! to GeeksforGeeks
GoodBye!! to Main function

Channels

(additional reference: https://www.geeksforgeeks.org/channel-in-golang/?ref=lbp)

Channels are a typed conduit through which you can send and receive values with the channel operator, <-. (Like a Java BlockingQueue)

Creating a Channel

In Go language, a channel is created using chan keyword and it can only transfer data of the same type, different types of data are not allowed to transport from the same channel.

1
var Channel_name chan Type

You can also create a channel using make() function using a shorthand declaration.

1
channel_name:= make(chan Type)

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Go program to illustrate 
// how to create a channel
package main

import "fmt"

func main() {

// Creating a channel
// Using var keyword
var mychannel chan int
fmt.Println("Value of the channel: ", mychannel)
fmt.Printf("Type of the channel: %T ", mychannel)

// Creating a channel using make() function
mychannel1 := make(chan int)
fmt.Println("\nValue of the channel1: ", mychannel1)
fmt.Printf("Type of the channel1: %T ", mychannel1)
}
1
2
3
4
5
6
Output:

Value of the channel:
Type of the channel: chan int
Value of the channel1: 0x432080
Type of the channel1: chan int

Sending and Receiving Data from a Channel

In Go language, channel work with two principal operations one is sending and another one is receiving, both the operations collectively known as communication. And the direction of <- operator indicates whether the data is received or send. In the channel, the send and receive operation block until another side is not ready by default. It allows goroutine to synchronize with each other without explicit locks or condition variables.

1
2
3
ch <- v    // Send v to channel ch.
v := <-ch // Receive from ch, and
// assign value to v.

(The data flows in the direction of the arrow.)

Like maps and slices, channels must be created and initialized before use:

1
ch := make(chan int)

By default, sends and receives block until the other side is ready. This allows goroutines to synchronize without explicit locks or condition variables.

Closing a Channel

You can also close a channel with the help of close() function. This is an in-built function and sets a flag which indicates that** no more value will be sent or read to this channel**.

1
close(ch)

Also, you can check the channel is open or close with the help of the given syntax:

1
ele, ok:= <- Mychannel	// ele would be the element read from it

Here, if the value of ok is true which means the channel is open so, read operations can be performed. And if the value of is false which means the channel is closed so, read operations are not going to perform.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// Go program to illustrate how 
// to close a channel using for
// range loop and close function
package main

import "fmt"

// Function
func myfun(mychnl chan string) {

for v := 0; v < 4; v++ {
mychnl <- "GeeksforGeeks"
}
close(mychnl) // disables reading and sending
}

// Main function
func main() {

// Creating a channel
c := make(chan string)

// calling Goroutine
go myfun(c)

// When the value of ok is
// set to true means the
// channel is open and it
// can send or receive data
// When the value of ok is set to
// false means the channel is closed
for {
res, ok := <-c
if ok == false {
fmt.Println("Channel Close ", ok)
break
}
fmt.Println("Channel Open ", res, ok)
}
}
1
2
3
4
5
6
7
# Output:

Channel Open GeeksforGeeks true
Channel Open GeeksforGeeks true
Channel Open GeeksforGeeks true
Channel Open GeeksforGeeks true
Channel Close false

You can also use an enhanced for loop to interate over the values stored in a channel until it is closed:

1
2
3
for item := range Chnl { 
// statements..
}

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Go program to illustrate how to 
// use for loop in the channel

package main

import "fmt"

// Main function
func main() {

// Creating a channel
// Using make() function
mychnl := make(chan string)

// Anonymous goroutine
go func() {
mychnl <- "GFG"
mychnl <- "gfg"
mychnl <- "Geeks"
mychnl <- "GeeksforGeeks"
close(mychnl)
}()

// Using for loop
for res := range mychnl {
fmt.Println(res)
}
}
1
2
3
4
5
6
# Output:

GFG
gfg
Geeks
GeeksforGeeks

Buffered Channels

Channels can be buffered. Provide the buffer length as the second argument to make to initialize a buffered channel:

1
ch := make(chan int, 100)	// means it can buffer/store 100 elements

Sends to a buffered channel block only when the buffer is full. Receives block when the buffer is empty.

You can find out the elements stored and the buffer length/capacity of a channel with len() and cap() function:

1
2
len(ch)	// returns the number of elements currently stored in a channel ch
cap(ch) // returns the maximum capacity/buffer length of a channel ch

Select and Case Statement in Channel

Select and case statement in Channel: In go language, select statement is just like a switch statement without any input parameter. This select statement is used in the channel to perform a single operation out of multiple operations provided by the case block.

A select blocks until one of its cases can run, then it executes that case. It chooses one at random if multiple are ready.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func main() {
ch1 := make(chan int, 1)
ch2 := make(chan int, 1)
ch1 <- 1
select {
case ch2 <- 20:
fmt.Println(<-ch1)
case ch1 <- 10:
fmt.Println("case 1")
}
}

However, you can also use the default case in a select is run if no other case is ready. This means you can use a default case to try a send or receive without blocking by placing it in a loop:

1
2
3
4
5
6
7
// the syntax
select {
case i := <-c:
// use i
default:
// receiving from c would block
}

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
"fmt"
"time"
)

func main() {
tick := time.Tick(100 * time.Millisecond)
boom := time.After(500 * time.Millisecond)
for {
select {
case <-tick:
fmt.Println("tick.")
case <-boom:
fmt.Println("BOOM!")
return
default:
fmt.Println(" .")
time.Sleep(50 * time.Millisecond)
}
}
}

sync.Mutex

But what if we don’t need communication? What if we just want to make sure only one goroutine can access a variable at a time to avoid conflicts?

This concept is called mutual exclusion, and the conventional name for the data structure that provides it is mutex. (This can be thought of being the synchronized keyword in Java).

Go’s standard library provides mutual exclusion with sync.Mutex and its two methods:

1
2
Lock()	// locks to only allow one goroutine to execute whatever lies below
Unlock() // releases the lock, should be done usually after the execution completes

We can define a block of code to be executed in mutual exclusion by surrounding it with a call to Lock and Unlock as shown on the Inc method.

We can also use defer to ensure the mutex will be unlocked as in the Value method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package main

import (
"fmt"
"sync"
"time"
)

// SafeCounter is safe to use concurrently.
type SafeCounter struct {
v map[string]int
mux sync.Mutex
}

// Inc increments the counter for the given key.
func (c *SafeCounter) Inc(key string) {
c.mux.Lock()
// Lock so only one goroutine at a time can access the map c.v.
c.v[key]++
c.mux.Unlock() // releases the lock
}

// Value returns the current value of the counter for the given key.
func (c *SafeCounter) Value(key string) int {
c.mux.Lock()
// Lock so only one goroutine at a time can access the map c.v.
defer c.mux.Unlock() // releases the lock after return
return c.v[key]
}

func main() {
c := SafeCounter{v: make(map[string]int)}
for i := 0; i < 1000; i++ {
go c.Inc("somekey")
}

time.Sleep(time.Second)
fmt.Println(c.Value("somekey"))
}