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
.profileor.zshrcor.bashrcdepending 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 typedlanguage. Because of that, it helps Go compiler to identify many types of errors atcompile timeeven 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 PackagesandLibrary Packages.
- To make a package executable, name that package
main. It's a special package. packageclause can be used only once per file and it should be the first line in.gosource file.- Package contains multiple
Gofiles 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 Packageshould also containmain()function and that too only once.- These are created only for
runningit as a Go program. - These cannot be imported into a Go program.
- Package name should be
main.
- Library Packages- Almost all
Go Standard Library Packagesare of typeLibrary Packages. - They are reusable packages.
- They are not executable packages. So we can't run them.
- We can only
importthem. - These are created only for
reusabilitypurposes. - 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 toinitializethestate of a package. - Go automatically calls
init()functionbeforecallingcommand-linepackage'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. declaredfuncsare onlyvisibleto 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 Typesliteralmeans thevalueitself. Unlinevariable, aliteraldoesn'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
nullin 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
stringis 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 valueto it based on it's variable type. - Type of a variable decides what
zero valueit 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 scopeare not allowed in Go since they causemaintenance nightmares. If we declare a variable inblocked scopethen we must use it or else completely remove it from the block. We cannot have unused variables declared inblocked scopedangling in our source codes. Go throws unused variable errors atcompile timeonly. - We should avoid using
package levelvariables. Go doesn't throwunused variable errorsatcompile timefor variables declared atpackage level.
- Multiple Declarations- Sometimes it is also called as parallel variable declarations.
- Declaring multiple variables with
different typesin a single statement:package main func main() { var ( adiBool bool adiInt int adiFloat float64 adiStr string adiPointer *string ) }
- Declaring multiple variables with
same typein a single statement:package main func main() { var foo, bar, baz int }
- Type InferenceType Inferencemeans 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
typespecification. - 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 ommittingvarkeyword along with it's variable type. - It
declaresandinitializesthe variable. - We cannot use
Short Declarationsyntax to declare variables inPackage Scope. - At
Package Scope, all declarations should start with akeyword. SinceShort Declarationsyntax doesn't have anykeywordin 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 variablesofdifferent typesusingshort declarationsyntax: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 DeclarationsShort Declarationcan initialize new variables and assign to existing variables at the same time.- At least one of the variable in
Short Declaration Redeclarationmust 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 variablesinblocked 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.Printfas 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 - PerformanceSliceoperations are cheap!Slicing: Creates a newslice header.Assigning a Slice to another Sliceor,passing it to a function: Only copies theslice header.Slice header has a fixed sizeandit doesn't changeeven if we have got millions of elements.Arraycan be expensive as compared toSlice.Assigning an array to another arrayorpassing it to a function: Copies all the elements of it.
+ Composite Types In Go- Following are the
Composite Typesin Go:- Arrays: Collection of elements.
IndexableandFixed Length. - Slices: Collection of elements.
IndexableandDynamic 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 Gocompilercan catchoverflow errors. - In runtime, when
overflowsoccurs:integerwrap arounds and go to their minimum and maximum values.floatwrap arounds topositive infinityornegative 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 typeis abuilt-in typethat we can use everywhere withoutimportinganypackage. - A
built-in typemeans it's acore feature of Goi.e. it comes withcompileritself. - A
predeclared typehas a name and we can use it inany scope. - We don't have to
declareapredeclared typebefore using it. - It has a
type representationi.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 bytesi.e. how much space it needs in memory and it also determines the range of values it can represent. - Go cannot catch
overflow errorsinruntime. For e.g. Avariablebelongs toruntimeand 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 typecan 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 Typeis also called asNamed Type. - A
Defined Typecan only be created from another existingType. - We need to give a new name to newly created type.
- Newly
Defined Typecan optionally have it's ownmethods. - A
typecan beconvertedtoanother typeif theysharethesame underlying typeand vice versa. - A
defined typeand it'ssource typeshare 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 Typesbyteanduint8are exactlythe same typesjust withdiferent names.runeandint32are exactlythe same typesjust withdiferent names. i.e.runeis analiasofint32. Therunetype is used to representunicode characters.Type Aliasdeclaration is not for everyday usage. It is mainly used in very huge codebase refactors.
Constantsbelong tocompile time. They must be initilized with value when they are declared.Constantsare created atcompile time. In therun time, Go just transforms it into avalue.- Unnamed constants: All
basic literalsareunnamed constants. Following are examples ofbasic literals:// Unnamed constants 1 3.14 "hello" true false
- Named Constants: All
named constantswill be replaced to theirvaluesinruntime. They need to bedeclaredfirst. - Untyped Constants:
Constantsmay 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
immutablei.e. we cannot change their values. - We
cannot initializea constant to aruntime value. - We can use
expressionswhile 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-numerictypes as well. - Constants don't have to be only
numeric values. - We
don't have todeclare thetypesof 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 DeclarationConstantsget theirtypesandexpressionsfromthe previous constant.- We can declare
multiple constantsin 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 literalsare alsotypeless. They all aretypeless constant values. - A
constant with a typecan only be used witha valueofthe same type. - The
untyped numeric constantcan be used withall numeric valuestogether. - 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 TypesConversiononly happens whena type is needed.- Go
convertsatypeless constantto atypedvalue when atypeis 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 convertsthetypeless constantto 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 constanthas adefault type. - Go
evaluates the expressionthen itconvertstheresulting typeless valueto itsdefault value.
+ IOTAIOTAis nothing but anumber generatorforconstants. In other words, it is ever increasing automatic counter.IOTAis built-inconstant generatorwhichgenerateseverincreasing numbers.IOTAstarts at0.- We can use
expressionswithIOTA. So, the otherconstantswillrepeattheexpressions. - 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
underscoresin names.
- In Go,
nilvalue 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") } }
+ nilnilis apredeclared identifierliketrue,false,len(),int32,float64etc.- Since it is a
predeclared identifier, it can be used anywherewithout importinganypackage. nil valuemeans that the value isnot initializedyet.- It is similar to following identifiers in other languages:
null // JavaScript None // Python null // Java nil // Ruby
- The
zero valueof allpointer-basedtypes in Go isnil. Following are thepointer-basedtypes in Go:pointers slices maps interfaces channels
- In Go,
nilvalue can beuntypedortypeddepending 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 valueis nothing but aseries of bytes. - We can represent a
string valueas 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 charactersasrune literals. NumbersandRune Literalsare the same thing.- In Go,
Unicode Code Pointsare calledRunes. - A
Rune literalis atypeless integer literal. - A
Rune literalcan be ofany integer type. for e.g.byte (uint8),rune (int32)orany other integer type. - In short,
Runeis aUnicode Code Pointthat is represented by anInteger Value. - Using
UTF-8we can representUnicode Code Pointsbetween1 byteand4 bytes. - We can represent any
Unicode Code Pointusing theRune Typebecause it can store4 bytesof data. For e.g.char := '🍺'
String valuesareread-only byte slicesi.e.string value ----> read-only []byte
String to Byte Sliceconversion creates anew []byte sliceand copies the bytes of the string to a new slice'sbacking array. They don't share the samebacking array.- In short,
Stringis animmutable byte sliceand we cannot change any of it's elements. However, we can convertstring to a byte sliceand then we can change thatnew slice. - A
stringis a data structure that points to aread-only backing array. UTF-8is avariable length encoding(for efficiency). So eachrunemay start at adifferent index.for rangeloop jumps over therunes of a string, rather than thebytes of a string. Eachindexreturns thestarting indexof thenext rune.Runesin aUTF-8 encoded stringcan have a different number ofbytesbecauseUTF-8is avariable byte-length encoding.- Especially in scripting languages, we can manipulate
UTF-8 stringsbyindexeseasily. However, Go doesn't allow us to do soby defaultbecause of efficiency reasons. - Go never hides the cost of doing something.
[]rune(string)creates anew slice, and copies eachruneto new slice'sbacking array. This is inefficient way of indexing strings.- A
stringvalue usually useUTF-8so it can be more efficient because eachruneon the other handuses 1 to 4 bytes(variable-byte length). - Each
runein[]rune(Rune Slice) has the same length i.e.4 bytes. It is inefficient because therunetype is an alias toint32. - In Go, if our
source code fileis encoded intoutf-8thenString Literalsin our file are automatically encoded intoutf-8. - When we're working with
bytes, continue working withbytes. Do not convert astringto[]byte(Byte Slice) or vice versa, unless necessary. Prefer working with[]byte(Byte Slice) whenever possible.Bytesare more efficient and used almost everywhere in Go standard libraries.
+ Maps 101Mapsallows us to quickly access to anelement/valueusing aunique key.Map keysmust beuniquebecause otherwise it can't find the correspondingvalues/elements.- The types of
Map KeysandValues in Mapscan bedifferent. - A
Map Keymust be acomparable type. - All
Map KeysandMap Valuesmustbelongto theircorresponding types. Theycan't be mixed up. - A
Map Variable (or a Value)is nothing but apointerto aMap Header Valuein thememory. - A
Map Valueonly contains thememory addressof aMap Header.
+ Inheritance vs. Composition+ Structs 101Structsareblueprints— They arefixedatcompile-time.- It's like a
classinOOPlanguages. Groupsrelated datain asingle type. Struct typesare created atcompile-time.- A
structmay storedifferent typesof data. Struct fieldsare declared atcompile-time. However,struct valuesfill them inruntime.- The
field namesandtypesare declared atcompile-time. They arefixedand cannot change inruntime. Field valuesbelong toruntime. We can change them inruntime.Structscannot dynamicallygrowbut they can havedifferentset oftypes.- Struct example:
type VideoGame struct { Title string Genre string Published bool }
- Two
structsareequalif all theirfieldsareequal. Anonymous Fields: When thefield names conflicttheparent typetakespriority.
+ MethodsMethodsenhancetypeswith additional behavior.Methodsof thetypeare 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
receiveris nothing but method'sinput parameterswrittenbeforeamethod name. - A
methodbelongs to asingle type. Methodsondifferent typescan have thesame names.Method Expressionsallows us to callmethodsthroughtypes. For e.g.// "game" is a struct type game.print(cod) game.print(battlefield)
- Behind the scenes, a
methodis afunctionthat takesreceiveras it'sfirst argument.
+ Pointer Receivers- We can define
methodsontypesusingPointer Receivers. - The only difference between
methodand afunctionis that amethodbelongs to atype, whereas afunctionbelongs to apackage. - Consistent Design Tip: When one of the
methodsin anytypeare usingpointer receiver, it is better to convert allmethod receiversof thattypetopointer receivers. - We (must) use a
pointer receiverwhen we want to make changes to areceiver variable. In other words, use apointer receiverwhen the received value intomethodis 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
Interfacemuch like as we define auser defined type. Interfacesdecouple differenttypesfrom each other so we can create more maintainable programs.- An
Interfaceis aProtocol, aContract. - Bigger the
Interfacethe weaker theabstraction. -->Rob Pike - It's an
abstract type. It doesn't have any implementation. It only describesthe expected behavior. - The opposite of
Abstract TypeisConcrete Type. - All the
typesin Go exceptInterfaceare 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 automaticallywhen it hasall the methods of the Interfacewithout explicitely specifying it.Interfacevalues arecomparable.Go interfacesareimplicit. 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 AssertionType Assertionallows us toextractthedynamic valuefromInterface.- It can also be used to check (assert) whether the
Interface Valueprovides themethodwe want.
+ Empty Interface- Do not use
Empty Interfaceunless really necessary. - Every
typein Go implements theempty interface. - An
Interface Valuehas 2 parts:- A dynamic
Value. - A dynamic
Type.
- A dynamic
Empty Interfaceis the one which doesn't have anymethods.- Every
Typesatisfies theEmpty Interface. - It can represent any
TypeofValue. - We can't directly use the
dynamic valueof anempty interface value. - Example of Empty Interface:
type someInterface interface { }
- To use a
valuefromEmpty Interface, we first need toextractit usingType Assertion. Empty Interface Slicecontains 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, ormapusing the emptyinterface{} type.
- A function that returns a value of
+ Type SwitchType Switchallows us to detect and extractdynamic valuesfromInterface ValuesusingSwitch Statement.- When we have lots of conditions to check, we can use
Type Switch. - Example of
Type Switchstatement:// "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
Switchwhich comparesvalues, theType Switchcomparestypes 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.
Concurrencyis 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:
Concurrencyis powerful.Concurrencyis notparallelism.Concurrencyenablesparallelism.Concurrencymakesparallelism(and scaling and everything else) easy.
+ ParallelismParallelismis 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.
Concurrencyis about dealing with lots of things at once.Parallelismis about doing lots of things at once.- Not the same, but related.
Concurrencyis aboutstructure,parallelismis aboutexecution.Concurrencyprovides a way tostructure a solutionto solve a problem that may (but not necessarily) beparallelizable.- An analogy
Concurrent: Mouse, keyboard, display, and disk drivers.Parallel: Vector dot product.
- In Go,
concurrencyis achieved by usingGoroutines. Goroutinesarefunctionsormethodswhich can runconcurrentlywith othersmethodsandfunctions.Goroutinesare lightweightthreadsthat are managed by theGo runtime.- They are very much similar like
threadsinJavabut 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
gobefore 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, allGoroutinesare terminated regardless of the fact if all theGoroutineshas completed execution or not. - We can also run
Anonymous FunctionsasGoroutinesas follows:// Executing anonymous function as Goroutine go func() { // }()
+ Advantages of Goroutines over ThreadsGoroutineshave a faster startup time thanthreads.Goroutinescome withbuilt-in primitivesto communicate safely between themselves called aschannels.Goroutinesare extremely cheap when compared tothreads. They are only a fewkbinstack sizeand thestackcan grow and shrink according to needs of the application whereas in the case ofthreadsthestack sizehas to be specified and isfixed.
Channelsare conduits (pipes) that we can use to pass values of a particulartypefrom oneGoroutineto another.Channelsare a mechanism for communication.ChannelsallowsGoroutinesto 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
Channelusing built-inmake()function as below:ch := make(chan type) // type: Data Type
Normal ChannelsareSynchronous. i.e. Both the sending side and the receiving side of thechannelwait until the other side is ready.
+ Buffered ChannelsBuffered ChannelsareAsynchronous. i.e. Sending and Receiving messages throughBuffered Channelswill not block unless theChannelis full.- We can create a
Buffered Channelsame way as we create theNormal Channelsusing 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 Capacityas1, we are creating aNormal Channel. To create aBuffered Channel, we have to passBuffering Capacityasgreater than 1
- Since
Goroutinesrun in asame address space, they have access toshared memoryand thisaccessmust besynchronised. Go's motto is toshare memorybycommunicating(GoroutinesandChannelsmakes this possible). - Sometimes, some problems are better suited to using the
traditional forms of synchronisation. Go allows us to make use of theseSynchonisation Primitivesby using theSyncpackage.
+ Mutexes- A
Race Conditionhappens when two or morethreadscan accessshared dataand try to change thatshared dataat the same time. We can useMutexto solve this problem. - A
Mutexis aMutual Exclusion Lock. It's aSynchronisation Primitive. - It is used to
protect shared datawhich is simultaneouslyaccessedbymultiple 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 GroupsWait Groupsare anotherSynchronisation Primitive.- A
Wait Groupbasically waits forcollectionofGoroutinesto finish execution.
go vetcommand 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
intis supplied tofmt.Printf()whereasstringvalue 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