In-depth internals, my personal notes, example codes and projects. Includes - Thousands of codes, OOP, Concurrency, Parallelism, Goroutines, Mutexes & Wait Groups, Testing in Go, Go tool chain, Backend web development, Some projects including Log file parser using bufio.Scanner, Spam Masker, Retro led clock, Console animations, Dictionary programs, Social Network built using Go and GopherJS, Database Connectivity and working (MySQL, MongoDB, Redis), GopherJS and lot more..
Aditya Hajare (Linkedin).
WIP (Work In Progress)!
Open-sourced software licensed under the MIT license.
- Uber Go Style Guide
- Concurrency Using Golang
- Protocol Buffers Using Golang
- REST Microservice | DDD | Gin Gonic | Testing
- REST Microservice | DDD | Gorilla/Mux
- REST Microservice | MVC | Bad Practices
- Go Configurations
+ Environment Configurations + VS Code Configurations
- Basics
+ Packages - Executable Packages - Library Packages + Function init() + Scopes + Renaming Imports + Exporting + Data Types - Basic Data Types + Variables - Zero Values - Unused variables - Multiple Declarations - Type Inference - Short Declaration - Multiple Short Declarations - Redeclarations With Short Declarations + Blank Identifier + fmt.Printf and fmt.Sprintf Formatting + Slice Vs. Array - Performance + Composite Types In Go
- Naming Conventions In Go
↗️ - Type System In Go
+ Important Links + Predeclared Types + Defined Types + Aliased Types
- Constants
+ Important Links + Constant Types + Multiple Constants Declaration + Typeless Or Untyped Constants + Default Types + IOTA + Common Abbreviations Used In Go
- Error Handling
+ nil
- Strings Runes And Bytes
+ Important Links + Strings Runes And Bytes 101
- Maps In Go
+ Maps 101
- Structs In Go
+ Inheritance vs. Composition + Structs 101
- OOP In Go With Methods And Interfaces
+ Methods + Pointer Receivers + Attaching Methods To Any Types + Interfaces + Type Assertion + Empty Interface + Type Switch
- Concurrency And Parallelism
+ Concurrency + Parallelism + Concurrency vs. parallelism
- Goroutines
+ Advantages of Goroutines over Threads
- Channels
+ Buffered Channels
- Mutexes And Wait Groups From GoSync Package
+ Mutexes + Wait Groups
- Go Vet
- Go Documentation Server On Local Machine
+ Environment Configurations
- Open up
.profile
or.zshrc
or.bashrc
depending on our OS and add/edit following:#!/bin/bash # Specifies where the Go destribution is installed on the system. export GOROOT=/usr/local/go # Specifies top-level directory containing source code for all our Go projects. # Inside this directory, we need to create 3 more directories viz. "src", "pkg" and "bin". export GOPATH=~/adiwork/go # This directory is also known as Go Workspace. # "src" directory inside Workspace represents where all the Go source code will be stored. # "pkg" directory inside Workspace represents where the compiled Go packages will be stored. # "bin" directory inside Workspace represents where the produced Go compiled binaries will be stored. # Specifies where Go should install compiled binaries. export GOBIN=${GOPATH}/bin # Attaching GOROOT and GOBIN to shell environment's path variable. export PATH=${PATH}:/usr/local/bin:${GOROOT}/bin:${GOBIN}
- Execute following command to get
stringer
:go get -u golang.org/x/tools/cmd/stringer
+ VS Code Configurations
- My VS Code configs for Go:
{ "go.lintTool": "golangci-lint", "go.formatTool": "goimports", "go.useLanguageServer": true, "go.lintOnSave": "package", "go.vetOnSave": "package", "go.vetFlags": [ "-all", "-shadow" ] }
- Go is a
strongly typed
language. Because of that, it helps Go compiler to identify many types of errors atcompile time
even before our program is run.
+ Packages
- All package files, should be in the same (single) directory. i.e. all package source code files should be located in a one single directory.
- All files in a specific folder should belong to a one single package. It's a convention, not a rule.
There are 2 kinds of packages in Go:
Executable Packages
andLibrary Packages
.
- To make a package executable, name that package
main
. It's a special package. package
clause can be used only once per file and it should be the first line in.go
source file.- Package contains multiple
Go
files belonging to same folder. - Any package that is intended to run on a
command-line
, must declarepackage main
. - To alias a package upon importing:
package main import fm "fmt" // Package "fmt" has been aliased as "fm" func main() { // }
- Executable Packages
- It's name should always be
package main
. Executable Package
should also containmain()
function and that too only once.- These are created only for
running
it as a Go program. - These cannot be imported into a Go program.
- Package name should be
main
.
- Library Packages
- Almost all
Go Standard Library Packages
are of typeLibrary Packages
. - They are reusable packages.
- They are not executable packages. So we can't run them.
- We can only
import
them. - These are created only for
reusability
purposes. - Package name can have any name.
- Doesn't need to have function named
main()
. To avoid confusion, it's better not to have function namedmain()
in a reusable package.
+ Function init()
- The
init()
function is used toinitialize
thestate of a package
. - Go automatically calls
init()
functionbefore
callingcommand-line
package'smain()
function.
+ Scopes
- Same name cannot be declared again inside a same scope.
- There are following types of scopes in Go:
package
: Each Go package has it's ownscope
. For e.g. declaredfuncs
are onlyvisible
to the files belonging to samepackage
.file
: Imported packages are only visible to the importing file. Each file has to import external packages on it's own.func
.block
.
+ Renaming Imports
- We can rename an imported package name with following syntax:
package main import "fmt" import adi "fmt" // Imported "fmt" package and renamed it to "adi" func main() { adi.Println("नमस्ते आदित्य") // This will print "नमस्ते आदित्य" }
- We can import packages with the same name into same file by giving one of them imports a new name.
+ Exporting
- To export a name in Go, just make it's first letter an uppercase letter.
- For e.g.
package aditest func Adi() { // 'Adi()' will be exported and will be available throughout 'aditest' package // Code.. } func adiNew() { // 'adiNew()' will not be exported since it's name doesn't start with uppercase letter. // Code }
+ Data Types
literal
means thevalue
itself. Unlinevariable
, aliteral
doesn't have a name.- There are following data types in Go:
- Basic type: Numbers, strings, and booleans come under this category.
- Aggregate type: Array and structs come under this category.
- Reference type: Pointers, slices, maps, functions, and channels come under this * category.
- Interface type
- Basic Data Types
- Following are the basic data types in Go:
- Numeric:
// Integer Types uint8 // Unsigned 8-bit integers (0 to 255) uint16 // Unsigned 16-bit integers (0 to 65535) uint32 // Unsigned 32-bit integers (0 to 4294967295) uint64 // Unsigned 64-bit integers (0 to 18446744073709551615) int8 // Signed 8-bit integers (-128 to 127) int16 // Signed 16-bit integers (-32768 to 32767) int32 // Signed 32-bit integers (-2147483648 to 2147483647) int64 // Signed 64-bit integers (-9223372036854775808 to 9223372036854775807) // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Floating Types float32 // IEEE-754 32-bit floating-point numbers float64 // IEEE-754 64-bit floating-point numbers complex64 // Complex numbers with float32 real and imaginary parts complex128 // Complex numbers with float64 real and imaginary parts // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Other Numeric Types byte // same as uint8 rune // same as int32 uint // 32 or 64 bits int // same size as uint uintptr // an unsigned integer to store the uninterpreted bits of a pointer value
- Boolean:
bool // Represents 'true' or 'false'
- String:
- In Go language, strings are different from other languages like Java, C++, Python, etc.
- Strings can't be
null
in Go. - It is a sequence of variable-width characters where each and every character is represented by one or more bytes using UTF-8 Encoding.
- In Go, a
string
is in effect is a read-only slice of bytes (immutable). - Or in other words, strings are the immutable chain of arbitrary bytes (including bytes with zero value) and the bytes of the strings can be represented in the Unicode text using UTF-8 encoding.
- String literals can be created in 2 ways:
- Using double quotes
- Using backticks
- Numeric:
+ Variables
- Variables in Go Lang
- In Go, we have to declare a variable before we can use it. This is required and necessary for the
compile time safety
. - Variables are not created at
compile time
. They are created atrun time
. - The unnamed variables are
pointers
(like in C). - Once we declare a type for a variable, it cannot be changed later. It is static.
- Zero Values
- When a variable is declared and it isn't assigned any value at the time of declaration, Go will assign a
zero value
to it based on it's variable type. - Type of a variable decides what
zero value
it will take initially when declared (and if it isn't assigned any value at the time of declaration).// Zero Values assigned to variables by Go when they are declared and not assigned any values at the time of declaration. var adiBool bool // false var adiInt int // 0 var adiFloat float64 // 0 var adiStr string // "" var adiPointer *string // nil | 'nil' means it doesn't point to any memory location
- Unused variables
- Unused variables in
blocked scope
are not allowed in Go since they causemaintenance nightmares
. If we declare a variable inblocked scope
then we must use it or else completely remove it from the block. We cannot have unused variables declared inblocked scope
dangling in our source codes. Go throws unused variable errors atcompile time
only. - We should avoid using
package level
variables. Go doesn't throwunused variable errors
atcompile time
for variables declared atpackage level
.
- Multiple Declarations
- Sometimes it is also called as parallel variable declarations.
- Declaring multiple variables with
different types
in a single statement:package main func main() { var ( adiBool bool adiInt int adiFloat float64 adiStr string adiPointer *string ) }
- Declaring multiple variables with
same type
in a single statement:package main func main() { var foo, bar, baz int }
- Type Inference
Type Inference
means Go can figure out the type of a variable automatically from it's assigned value.- If we are assigning value to a variable at the time of it's declaration, we can ommit it's
type
specification. - For e.g.
package main main() { var someFlag = true // We are not specifying type of 'someFlag' as bool here. }
- Short Declaration
- With
Type Inference
, Go can figure out variable type based off it's assigned value. - In
Short Declaration
, we can declare variable by completely ommittingvar
keyword along with it's variable type. - It
declares
andinitializes
the variable. - We cannot use
Short Declaration
syntax to declare variables inPackage Scope
. - At
Package Scope
, all declarations should start with akeyword
. SinceShort Declaration
syntax doesn't have anykeyword
in it, it doesn't work atPackage Scope
. - For e.g.
package main main() { someFlag := true // 'var' keyword and 'variable type' is not specified. It works! }
- Multiple Short Declarations
- We can declare and initialize
multiple variables
ofdifferent types
usingshort declaration
syntax:package main main() { someFlag, age, name := true, 30, "आदित्य" // Multiple variables of different types. }
- In this type of declaration, number of values and number of names must be the same. Otherwise it will result in error.
- Redeclarations With Short Declarations
Short Declaration
can initialize new variables and assign to existing variables at the same time.- At least one of the variable in
Short Declaration Redeclaration
must be a new variable. - For e.g.
package main main() { var someFlag bool // someFlag := true // Error! At least one variable must be new to make this work. someFlag, age := true, 30 // This works! Because 'age' is a new variable being declared in the same statement. someFlag will be set (redeclared) to true. }
+ Blank Identifier
“There are only two hard things in Computer Science: cache invalidation and naming things”. Tim Bray quoting Phil Karlton
- Go doesn't allow
unused variables
inblocked scope
. - To ignore a variable,
Blank Identifier (_)
is used as a variable name in Go. - Go compiler will not throw unsed variable error if a blocked scope variable is named
_
. - We cannot use value assigned to
_
. - It is like a black hole that swallows variable.
- Detailed information and usage of Blank Identifier
+ fmt.Printf and fmt.Sprintf Formatting
- Following formatting can be used with
fmt.Printf
as well asfmt.Sprintf
:// String and slice of bytes %s // the uninterpreted bytes of the string or slice %q // a double-quoted string safely escaped with Go syntax %x // base 16, lower-case, two characters per byte %X // base 16, upper-case, two characters per byte // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Boolean %t // the word true or false // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // General %v // The value in a default format. When printing structs, the plus flag (%+v) adds field names. %#v // a Go-syntax representation of the value %T // a Go-syntax representation of the type of the value %% // a literal percent sign; consumes no value // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Integer %b // base 2 %c // the character represented by the corresponding Unicode code point %d // base 10 %o // base 8 %q // a single-quoted character literal safely escaped with Go syntax %x // base 16, with lower-case letters for a-f %X // base 16, with upper-case letters for A-F %U // Unicode format: U+1234; same as "U+%04X" // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // The default format for %v bool // %t int, int8 // %d uint, uint8 // %d, %x if printed with %#v float32, complex64 // %g string // %s chan // %p pointer // %p // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Floating-point and complex constituents %b // decimalless scientific notation with exponent a power of two, in the manner of strconv.FormatFloat with the 'b' format, e.g. -123456p-78 %e // scientific notation, e.g. -1.234456e+78 %E // scientific notation, e.g. -1.234456E+78 %f // decimal point but no exponent, e.g. 123.456 %F // synonym for %f %g // %e for large exponents, %f otherwise %G // %E for large exponents, %F otherwise // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Floating-point Precision %f // default width, default precision %9f // width 9, default precision %.2f // default width, precision 2 %9.2f // width 9, precision 2 %9.f // width 9, precision 0 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Pointer %p // base 16 notation, with leading 0x // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Other flags + // always print a sign for numeric values; guarantee ASCII-only output for %q (%+q). - // pad with spaces on the right rather than the left (left-justify the field). # // alternate format: add leading 0 for octal (%#o), 0x for hex (%#x); 0X for hex (%#X); suppress 0x for %p (%#p); for %q, print a raw (backquoted) string if strconv.CanBackquote returns true; ' ' (space) // leave a space for elided sign in numbers (% d); put spaces between bytes printing strings or slices in hex (% x, % X). 0 // pad with leading zeros rather than spaces; for numbers, this moves the padding after the sign.
+ Slice Vs. Array - Performance
Slice
operations are cheap!Slicing
: Creates a newslice header
.Assigning a Slice to another Slice
or,passing it to a function
: Only copies theslice header
.Slice header has a fixed size
andit doesn't change
even if we have got millions of elements.Array
can be expensive as compared toSlice
.Assigning an array to another array
orpassing it to a function
: Copies all the elements of it.
+ Composite Types In Go
- Following are the
Composite Types
in Go:- Arrays: Collection of elements.
Indexable
andFixed Length
. - Slices: Collection of elements.
Indexable
andDynamic Length
. - Strings: Byte Slices. ASCII and UNICODE.
- Maps: Collection of
Indexable Key-Value Pairs
. - Structs: Groups different types of variables together.
- Arrays: Collection of elements.
- At
compile time
, a Gocompiler
can catchoverflow errors
. - In runtime, when
overflows
occurs:integer
wrap arounds and go to their minimum and maximum values.float
wrap arounds topositive infinity
ornegative infinity
.
+ Important Links
- What is Binary?
- What's a Bit
- What's a Byte
- How old school graphics worked? PART 1
- How old school graphics worked? PART 2
- Stackoverflow: What actually is a Byte Stream?
- Why Byte but not uint8?
+ Predeclared Types
- A
predeclared type
is abuilt-in type
that we can use everywhere withoutimporting
anypackage
. - A
built-in type
means it's acore feature of Go
i.e. it comes withcompiler
itself. - A
predeclared type
has a name and we can use it inany scope
. - We don't have to
declare
apredeclared type
before using it. - It has a
type representation
i.e. how Go see it and how we can use it. In other words,what values a type can represent
. - It has a
size in bytes
i.e. how much space it needs in memory and it also determines the range of values it can represent. - Go cannot catch
overflow errors
inruntime
. For e.g. Avariable
belongs toruntime
and it's value cannot be known at thecompile time
. - In Go, when variable values
overflow
, they gets wrapped around i.e. They get reassigned to the minimum value theirvariable type
can represent. - Examples of
Predeclared Types
:bool // 'bool' is a predeclared type and it has following characteristics: // Name: bool // Representation: 'true' or 'false' // Size: 1 byte int // 'int' is a predeclared type and it has following characteristics: // Name: int // Representation: -1, 0, 1, 1000000000000000 // Size: 8 byte
+ Defined Types
- A
Defined Type
is also called asNamed Type
. - A
Defined Type
can only be created from another existingType
. - We need to give a new name to newly created type.
- Newly
Defined Type
can optionally have it's ownmethods
. - A
type
can beconverted
toanother type
if theyshare
thesame underlying type
and vice versa. - A
defined type
and it'ssource type
share the sameunderlying type
. - For e.g.
// 'Duration' is the 'defined type' or 'named type'. // 'int64' is the 'underlying type'. type Duration int64 // Type conversion var microSeconds int64 // 'microSeconds' variable is of type 'int64' var nanoSeconds Duration // 'nanoSeconds' variable is of type 'Duration' nanoSeconds = microSeconds // ERROR! This won't work. To make it work: nanoSeconds = Duration(microSeconds) // Works! We are converting 'microSeconds' to 'Named Type' we have created above i.e. 'Duration' microSeconds = int64(nanoSeconds) // This also works!
+ Aliased Types
byte
anduint8
are exactlythe same types
just withdiferent names
.rune
andint32
are exactlythe same types
just withdiferent names
. i.e.rune
is analias
ofint32
. Therune
type is used to representunicode characters
.Type Alias
declaration is not for everyday usage. It is mainly used in very huge codebase refactors.
Constants
belong tocompile time
. They must be initilized with value when they are declared.Constants
are created atcompile time
. In therun time
, Go just transforms it into avalue
.- Unnamed constants: All
basic literals
areunnamed constants
. Following are examples ofbasic literals
:// Unnamed constants 1 3.14 "hello" true false
- Named Constants: All
named constants
will be replaced to theirvalues
inruntime
. They need to bedeclared
first. - Untyped Constants:
Constants
may or may not have a type. - If the value is'nt going to change throughout our program's lifetime and we already know the value (if it belongs to compile time) then we should go for
named constants
. - Constants are
immutable
i.e. we cannot change their values. - We
cannot initialize
a constant to aruntime value
. - We can use
expressions
while initializingconstants
.
+ Important Links
- Go’s typed and untyped constants
- Go enums and iota — Full of tips and tricks with runnable code examples
+ Constant Types
- We can declare constants using
non-numeric
types as well. - Constants don't have to be only
numeric values
. - We
don't have to
declare thetypes
of constants. - For e.g.
func main() { // Below works.. const min int = 1 const pi float64 = 3.14 const version string = "2.0.3" const debug bool = true // Declaring constants without types also works. const min = 1 const pi = 3.14 const version = "2.0.3" const debug = true // We can use expressions while initializing constants. const min = 1 + 1 //2 const pi = 3.14 * min // 6.28 const version = "2.0.3" + "-beta" // 2.0.3-beta const debug = !true // false }
+ Multiple Constants Declaration
Constants
get theirtypes
andexpressions
fromthe previous constant
.- We can declare
multiple constants
in a single go as below:func main() { // Multiple constants of same type in one go const min, max int = 1, 1000 // Declaring in group const ( min int = 1 max int = 1000 ) // Constants get their types and expressions from the previous constant const ( min int = 1000 // 1000 max // 1000 ) }
+ Typeless Or Untyped Constants
- When we declare
a constant without a type
, it becomesuntyped constant (typeless constant)
. - All
basic literals
are alsotypeless
. They all aretypeless constant values
. - A
constant with a type
can only be used witha value
ofthe same type
. - The
untyped numeric constant
can be used withall numeric values
together. - For e.g.
func main() { const min = 42 var i int = min // Type of constant 'min' = int var f float64 = min // Type of constant 'min' = float64 var b byte = min // Type of constant 'min' = byte var j int32 = min // Type of constant 'min' = int32 var r rune = min // Type of constant 'min' = rune }
+ Default Types
Conversion
only happens whena type is needed
.- Go
converts
atypeless constant
to atyped
value when atype
is needed. - For e.g.
func main() { const min int32 = 1000 max := 5 + min // Type of 'max' is 'int32' // Internally this happens: max := int32(5) + min }
- Go
implicitly converts
thetypeless constant
to atyped value
. - For e.g.
func main() { const min = 1000 max := 5 + min // Type of 'max' is 'int' // Internally this happens: max := int(5) + int(min) }
- An
untyped constant
has adefault type
. - Go
evaluates the expression
then itconverts
theresulting typeless value
to itsdefault value
.
+ IOTA
IOTA
is nothing but anumber generator
forconstants
. In other words, it is ever increasing automatic counter.IOTA
is built-inconstant generator
whichgenerates
everincreasing numbers
.IOTA
starts at0
.- We can use
expressions
withIOTA
. So, the otherconstants
willrepeat
theexpressions
. - We can use blank identifier (_) to adjust the values of constants:
func main() { const ( EST = -(5 + iota) // -5 _ // -6 | Discarded/skipped due to blank identifier MST // -7 PST // -8 ) }
+ Common Abbreviations Used In Go
- Following are some of the common Abbreviations used in Go standard libraries:
var s string // string var i int // index var num int // number var msg string // message var v string // value var val string // value var fv string // flag value var err error // error value var args []string // arguments var seen bool // has seen? var parsed bool // parsing ok? var buf []byte // buffer var off int // offset var op int // operation var opRead int // read operation var m int // another number var c int // capacity var c int // character var sep string // separator var src int // source var dst int // destination var b byte // byte var b []byte // buffer var buf []byte // buffer var w io.Writer // writer var r io.Reader // reader var pos int // position // ...list goes on and on...
- Use the complete words in larger scopes such as
package scope
. - Use
mixedCaps
. - Use all capital letters for common acronyms such as
API
. - Do not use
underscores
in names.
- In Go,
nil
value is extensively used for Error Handling. - For e.g.
func main() { data, err := someFunc() if err != nil { fmt.Println("Error occurred") return } else { fmt.Println("Success") } }
+ nil
nil
is apredeclared identifier
liketrue
,false
,len()
,int32
,float64
etc.- Since it is a
predeclared identifier
, it can be used anywherewithout importing
anypackage
. nil value
means that the value isnot initialized
yet.- It is similar to following identifiers in other languages:
null // JavaScript None // Python null // Java nil // Ruby
- The
zero value
of allpointer-based
types in Go isnil
. Following are thepointer-based
types in Go:pointers slices maps interfaces channels
- In Go,
nil
value can beuntyped
ortyped
depending on thecontext
.
+ Important Links
- Representing letters with numbers - Overview of ASCII and Unicode
- Characters in a computer - Advanced technical videos about the underlyings of ASCII and Unicode
- The 3rd video is especially important because it talks about UTF-8 encoding and decoding.
- Hexadecimal Number System - Hexadecimal numbers are important when working with bytes
- Go Blog: Strings
+ Strings Runes And Bytes 101
- A
string value
is nothing but aseries of bytes
. - We can represent a
string value
as abyte slice
. For e.g."hey" // String value []byte{104, 101, 121} // Representing string "hey" in byte slice []byte("hey") // Converting string "hey" into byte slice string([]byte{104, 101, 121}) // Converting byte slice into string value
- Instead of
numbers
(byte slice), we can also representstring characters
asrune literals
. Numbers
andRune Literals
are the same thing.- In Go,
Unicode Code Points
are calledRunes
. - A
Rune literal
is atypeless integer literal
. - A
Rune literal
can be ofany integer type
. for e.g.byte (uint8)
,rune (int32)
orany other integer type
. - In short,
Rune
is aUnicode Code Point
that is represented by anInteger Value
. - Using
UTF-8
we can representUnicode Code Points
between1 byte
and4 bytes
. - We can represent any
Unicode Code Point
using theRune Type
because it can store4 bytes
of data. For e.g.char := '🍺'
String values
areread-only byte slices
i.e.string value ----> read-only []byte
String to Byte Slice
conversion creates anew []byte slice
and copies the bytes of the string to a new slice'sbacking array
. They don't share the samebacking array
.- In short,
String
is animmutable byte slice
and we cannot change any of it's elements. However, we can convertstring to a byte slice
and then we can change thatnew slice
. - A
string
is a data structure that points to aread-only backing array
. UTF-8
is avariable length encoding
(for efficiency). So eachrune
may start at adifferent index
.for range
loop jumps over therunes of a string
, rather than thebytes of a string
. Eachindex
returns thestarting index
of thenext rune
.Runes
in aUTF-8 encoded string
can have a different number ofbytes
becauseUTF-8
is avariable byte-length encoding
.- Especially in scripting languages, we can manipulate
UTF-8 strings
byindexes
easily. However, Go doesn't allow us to do soby default
because of efficiency reasons. - Go never hides the cost of doing something.
[]rune(string)
creates anew slice
, and copies eachrune
to new slice'sbacking array
. This is inefficient way of indexing strings.- A
string
value usually useUTF-8
so it can be more efficient because eachrune
on the other handuses 1 to 4 bytes
(variable-byte length). - Each
rune
in[]rune
(Rune Slice) has the same length i.e.4 bytes
. It is inefficient because therune
type is an alias toint32
. - In Go, if our
source code file
is encoded intoutf-8
thenString Literals
in our file are automatically encoded intoutf-8
. - When we're working with
bytes
, continue working withbytes
. Do not convert astring
to[]byte
(Byte Slice) or vice versa, unless necessary. Prefer working with[]byte
(Byte Slice) whenever possible.Bytes
are more efficient and used almost everywhere in Go standard libraries.
+ Maps 101
Maps
allows us to quickly access to anelement/value
using aunique key
.Map keys
must beunique
because otherwise it can't find the correspondingvalues/elements
.- The types of
Map Keys
andValues in Maps
can bedifferent
. - A
Map Key
must be acomparable type
. - All
Map Keys
andMap Values
mustbelong
to theircorresponding types
. Theycan't be mixed up
. - A
Map Variable (or a Value)
is nothing but apointer
to aMap Header Value
in thememory
. - A
Map Value
only contains thememory address
of aMap Header
.
+ Inheritance vs. Composition
+ Structs 101
Structs
areblueprints
— They arefixed
atcompile-time
.- It's like a
class
inOOP
languages. Groupsrelated data
in asingle type
. Struct types
are created atcompile-time
.- A
struct
may storedifferent types
of data. Struct fields
are declared atcompile-time
. However,struct values
fill them inruntime
.- The
field names
andtypes
are declared atcompile-time
. They arefixed
and cannot change inruntime
. Field values
belong toruntime
. We can change them inruntime
.Structs
cannot dynamicallygrow
but they can havedifferent
set oftypes
.- Struct example:
type VideoGame struct { Title string Genre string Published bool }
- Two
structs
areequal
if all theirfields
areequal
. Anonymous Fields
: When thefield names conflict
theparent type
takespriority
.
+ Methods
Methods
enhancetypes
with additional behavior.Methods
of thetype
are calledMethod Set
.- To attach method to a
type
:// Syntax // "varName Type" is called a "receiver" func (varName Type) funcName() { // Code } // Example // "book" is a struct here func (b book) printBook() { fmt.Println(b.title, b.price) }
- A
receiver
is nothing but method'sinput parameters
writtenbefore
amethod name
. - A
method
belongs to asingle type
. Methods
ondifferent types
can have thesame names
.Method Expressions
allows us to callmethods
throughtypes
. For e.g.// "game" is a struct type game.print(cod) game.print(battlefield)
- Behind the scenes, a
method
is afunction
that takesreceiver
as it'sfirst argument
.
+ Pointer Receivers
- We can define
methods
ontypes
usingPointer Receivers
. - The only difference between
method
and afunction
is that amethod
belongs to atype
, whereas afunction
belongs to apackage
. - Consistent Design Tip: When one of the
methods
in anytype
are usingpointer receiver
, it is better to convert allmethod receivers
of thattype
topointer receivers
. - We (must) use a
pointer receiver
when we want to make changes to areceiver variable
. In other words, use apointer receiver
when the received value intomethod
is going to be very large.
+ Attaching Methods To Any Types
- We can attach methods to any type in Go. For e.g.
// Basic Types int string float64 // Bare Types array struct // ----------------------- // Do not use "Pointer Receivers" with below types since they already carry a pointer with themselves. // i.e. slice, map, chan, func // ----------------------- // Pointer Bearing Types slice map chan // Channels // We can also attach methods to: func
+ Interfaces
- We declare an
Interface
much like as we define auser defined type
. Interfaces
decouple differenttypes
from each other so we can create more maintainable programs.- An
Interface
is aProtocol
, aContract
. - Bigger the
Interface
the weaker theabstraction
. -->Rob Pike
- It's an
abstract type
. It doesn't have any implementation. It only describesthe expected behavior
. - The opposite of
Abstract Type
isConcrete Type
. - All the
types
in Go exceptInterface
are ofConcrete Type
. - For e.g. Following are
Concrete Types
:// Concrete Types int string float64 array struct slice map chan func
- The Interface only defines the expected behavior.
- Go does not have an implements keyword.
A Type satisfies an Interface automatically
when it hasall the methods of the Interface
without explicitely specifying it.Interface
values arecomparable
.Go interfaces
areimplicit
. The implementing types don't need to specify that they implement an interface.- Interface declaration example:
type MyInterface interface { foo() int bar() float64 baz() string }
+ Type Assertion
Type Assertion
allows us toextract
thedynamic value
fromInterface
.- It can also be used to check (assert) whether the
Interface Value
provides themethod
we want.
+ Empty Interface
- Do not use
Empty Interface
unless really necessary. - Every
type
in Go implements theempty interface
. - An
Interface Value
has 2 parts:- A dynamic
Value
. - A dynamic
Type
.
- A dynamic
Empty Interface
is the one which doesn't have anymethods
.- Every
Type
satisfies theEmpty Interface
. - It can represent any
Type
ofValue
. - We can't directly use the
dynamic value
of anempty interface value
. - Example of Empty Interface:
type someInterface interface { }
- To use a
value
fromEmpty Interface
, we first need toextract
it usingType Assertion
. Empty Interface Slice
contains theEmpty Interface Values
.- Example use cases of empty interfaces:
- A function that returns a value of
interface{}
can return anytype
. - We can store heterogeneous values in an
array
,slice
, ormap
using the emptyinterface{} type
.
- A function that returns a value of
+ Type Switch
Type Switch
allows us to detect and extractdynamic values
fromInterface Values
usingSwitch Statement
.- When we have lots of conditions to check, we can use
Type Switch
. - Example of
Type Switch
statement:// "v" ---> Interface Value // "type" ---> Extracts type from the Interface Value "v" // "e" ---> Extracted value will be assigned to variable "e". It changes depending on the extracted value. switch e := v.(type) { case int: // "e" is an "int" here.. case string: // "e" is an "string" here.. default: // "e"'s type equals to "v"'s type.. }
- Unlike regular
Switch
which comparesvalues
, theType Switch
comparestypes of the values
.
+ Concurrency
- The composition of independently executing tasks.
- Applied when dealing with handling lots of things at once.
- The focus is on how to structure a solution to solve a problem which may or may not be solved in a parallel manner.
Concurrency
is a way to structure a program by breaking it into pieces that can be executed independently.- Communication is the means to coordinate the independent executions.
- Go supports concurrency. Go provides:
- concurrent execution (
goroutines
). - synchronization and messaging (
channels
). - multi-way concurrent control (
select
).
- concurrent execution (
- IMPORTANT POINTS:
Concurrency
is powerful.Concurrency
is notparallelism
.Concurrency
enablesparallelism
.Concurrency
makesparallelism
(and scaling and everything else) easy.
+ Parallelism
Parallelism
is the simultaneous execution of computations.- Programming as the simultaneous execution of (possibly related) computations.
- It's all about doing lots of things at once.
+ Concurrency vs. parallelism
- “Concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once.” — Rob Pike
- Concurrency is a property of a program where two or more tasks can be in progress simultaneously. Parallelism is a run-time property where two or more tasks are being executed simultaneously. Through concurrency you want to define a proper structure to your program. Concurrency can use parallelism for getting its job done but remember parallelism is not the ultimate goal of concurrency.
Concurrency
is about dealing with lots of things at once.Parallelism
is about doing lots of things at once.- Not the same, but related.
Concurrency
is aboutstructure
,parallelism
is aboutexecution
.Concurrency
provides a way tostructure a solution
to solve a problem that may (but not necessarily) beparallelizable
.- An analogy
Concurrent
: Mouse, keyboard, display, and disk drivers.Parallel
: Vector dot product.
- In Go,
concurrency
is achieved by usingGoroutines
. Goroutines
arefunctions
ormethods
which can runconcurrently
with othersmethods
andfunctions
.Goroutines
are lightweightthreads
that are managed by theGo runtime
.- They are very much similar like
threads
inJava
but light weight and cost of creating them is very low. - When we run a function as a
Goroutine
, we are running the functionconcurrently
. - Place the keyword
go
before a function call to execute it as aGoroutine
. - To run a method or function concurrently prefix it with keyword
go
. - For e.g.
package main import ( "fmt" "time" ) func print() { fmt.Println("Printing from goroutine") } func main() { go print() time.Sleep(1 * time.Second) fmt.Println("Printing from main") }
- Program is terminated when
main()
function execution is completed. When the program terminates, allGoroutines
are terminated regardless of the fact if all theGoroutines
has completed execution or not. - We can also run
Anonymous Functions
asGoroutines
as follows:// Executing anonymous function as Goroutine go func() { // }()
+ Advantages of Goroutines over Threads
Goroutines
have a faster startup time thanthreads
.Goroutines
come withbuilt-in primitives
to communicate safely between themselves called aschannels
.Goroutines
are extremely cheap when compared tothreads
. They are only a fewkb
instack size
and thestack
can grow and shrink according to needs of the application whereas in the case ofthreads
thestack size
has to be specified and isfixed
.
Channels
are conduits (pipes) that we can use to pass values of a particulartype
from oneGoroutine
to another.Channels
are a mechanism for communication.Channels
allowsGoroutines
to share memory by communicating- We can use
Channel Operators: <-, ->
to send and receive values.- NOTE: The data flows in the direction of the
arrow
.
- NOTE: The data flows in the direction of the
- We can create a
Channel
using built-inmake()
function as below:ch := make(chan type) // type: Data Type
Normal Channels
areSynchronous
. i.e. Both the sending side and the receiving side of thechannel
wait until the other side is ready.
+ Buffered Channels
Buffered Channels
areAsynchronous
. i.e. Sending and Receiving messages throughBuffered Channels
will not block unless theChannel
is full.- We can create a
Buffered Channel
same way as we create theNormal Channels
using the built-inmake()
function. The only difference is, we can pass the second parameter tomake()
function which indicates the Buffered Channel'sBuffering Capacity
. - For e.g.
ch := make(chan type, capacity)
- NOTE: If we pass the
Buffering Capacity
as1
, we are creating aNormal Channel
. To create aBuffered Channel
, we have to passBuffering Capacity
asgreater than 1
- Since
Goroutines
run in asame address space
, they have access toshared memory
and thisaccess
must besynchronised
. Go's motto is toshare memory
bycommunicating
(Goroutines
andChannels
makes this possible). - Sometimes, some problems are better suited to using the
traditional forms of synchronisation
. Go allows us to make use of theseSynchonisation Primitives
by using theSync
package.
+ Mutexes
- A
Race Condition
happens when two or morethreads
can accessshared data
and try to change thatshared data
at the same time. We can useMutex
to solve this problem. - A
Mutex
is aMutual Exclusion Lock
. It's aSynchronisation Primitive
. - It is used to
protect shared data
which is simultaneouslyaccessed
bymultiple treads
. - Making changes to shared data and reading a shared data (e.g. for printing purposes) are still considered accessing the same data simultaneously.
+ Wait Groups
Wait Groups
are anotherSynchronisation Primitive
.- A
Wait Group
basically waits forcollection
ofGoroutines
to finish execution.
go vet
command helps us catch errors which are not generally caught by Go Compiler.- For e.g.
- Go to directory:
62-Go-Vet-To-Catch-Errors
- Execute file with below command:
go vet main.go
- It will catch the error where
int
is supplied tofmt.Printf()
whereasstring
value should've been supplied. This error isn't caught by Go Compiler since the program is still syntactically correct.
- Go to directory:
- To start a Go Documentation Server On Local Machine, execute:
# Install godoc with following command first: # go get golang.org/x/tools/cmd/godoc # Then execute: godoc -http=:6060
- In browser, visit:
http://localhost:6060