You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
nil for pointers, functions, interfaces, slices, channels, and maps
Literals
Explicitly-specified number, character, or string
4 common kinds of literals
Integer literal
Float Literal
Rune Literal
String Literal
Literals are considered untyped but have default type
Situations where the type is not explicitly declared but inferred from an initial value
In those cases, Go uses the default type for a literal
If a type cannot be determined from an expression, the literal defaults to a default type
E.g. Should 3 be used as a Float or an Integer?
Integer Literal
Sequence of numbers
Base 10 by default
Different prefixes can be used to specify other bases
0b or 0B => Binary
0x or 0X => Hexadecimal
0o or 0O => Octal
NOTE: 0 by itself can also be used for Octal but it is confusing so do not use it
Optional _ can be used as digit-separator
But cannot be at the beginning or end
Cannot be next to each other
Can be anywhere else in the number
It is better to only use them to improve readability at 1000s-boundaries
Or at byte-boundaries for binary, octal, or hexadecimal
In practice, use Base-10 to represent integers
Octal is mostly only used for POSIX permission flags
Hexadecimal and Binary are sometimes used for bit filters or networking and infrastructure applications
// Example of Integer Literals// ---------------------------fmt.Println("Example of Integer Literals:")
fmt.Println("----------------------------")
varageint=45varblueint=0x0000ffvaradminint=0o777varbillionint=1_000_000_000varredint=0xff_00_00fmt.Println("age =", age)
fmt.Println("blue =", blue)
fmt.Println("admin =", admin)
fmt.Println("billion =", billion)
fmt.Println("red =", red)
Float Literal
Sequence of numbers with decimal point to indicate the fractional portion
Can also have an exponent e or E with positive or negative number
Can also be written in hexadecimal
E.g. 0x12.34p5
p indicates the exponent
This is equivalent to 582.5 in decimal
Optional _ can be used as separators
Same rules as with integers
In practice, use Base-10 to represent floats
// Example of Float Literals// -------------------------fmt.Println("Example of Float Literals:")
fmt.Println("--------------------------")
varlengthfloat32=24.68varavogadrofloat64=6.2214e23varhexaFloatfloat64=0x12.34p5varbankBalancefloat64=10_314.56fmt.Println("length =", length)
fmt.Println("avogadro =", avogadro)
fmt.Println("hexaFloat =", hexaFloat)
fmt.Println("bankBalance =", bankBalance)
Rune Literal
Represent a single Unicode Codepoint
Surrounded by single-quotes ''
In Go, '' and "" are not interchangeable
'' is for Runes
"" is for Strings
Rune literals can be written in many ways
Rune Literal
Example
Single Unicode Character
'a'
8-Bit Octal Number
'\141'
8-Bit Hexadecimal Number
'\x61'
16-Bit Hexadecimal Number (Must use \u)
'\u0061'
32-Bit Unicode Number (Must use \U)
'\U00000061'
There are several backslash-escaped runes as well
Newline \n
Tab \t
Single-quote \'
Backslash \\
Avoid using any of the numeric-escapes for rune literals
Unless the context makes the code clearer
// Example of Rune Literals// ------------------------fmt.Println("Example of Rune Literals:")
fmt.Println("-------------------------")
vargenderrune='M'varnewlinerune='\n'vartabrune='\t'varquoterune='\''// As 8-Bit Octal NumbervarninetySevenrune='\141'// As 8-Bit Hexadecimal NumberninetySeven='\x61'// As 16-Bit Hexadecimal NumberninetySeven='\u0061'// 32-Bit Unicode NumberninetySeven='\U00000061'fmt.Println("gender =", gender)
fmt.Println("newline =", newline)
fmt.Println("tab =", tab)
fmt.Println("quote =", quote)
fmt.Println("ninetySeven =", ninetySeven)
String Literal
2 ways:
Interpreted String Literal ""
Raw String Literal ``
Interpreted String Literal is surrounded by double-quotes ""
Contains zero or more rune literals
They interpret rune literals into single characters
The following cannot appear in Interpreted String Literal
Unescaped backslash \
Unescaped newlines n
Unescaped double-quote "
In these cases, using Raw String Literal is more appropriate
Delimited with backticks ``
Can contain any character except backticks
If ` is required in raw string, try using `some raw string` + "`" approach instead
No escape character can be applied in raw strings: All characters are included as-is
// Example of String Literals// --------------------------fmt.Println("Example of String Literals:")
fmt.Println("---------------------------")
// Regular stringvargreetingsstring="Hello World!"// Using EscapesvargreetingsLongstring="Greetings and \n\"Salutations\"!"varwinSysPathstring="C:\\Windows\\System32"// Using Raw String LiteralvargreetingsRawstring=`Greetings and"Salutations"!`varwinUserPathstring=`C:\Users\<CurrentUserName>`varrawStringWithBacktickstring=`Backticks `+"(`) "+`cannot appear in raw string.For that, use "" to contain the `+"(`)"+`, then concatenate.`fmt.Println("greetings =", greetings)
fmt.Println("greetingsLong =", greetingsLong)
fmt.Println("winSysPath =", winSysPath)
fmt.Println("greetingsRaw =", greetingsRaw)
fmt.Println("winUserPath =", winUserPath)
fmt.Println("rawStringWithBacktick =", rawStringWithBacktick)
NOTE: \' is not legal in string literals
It gives an error error: unknown escape sequence: '
It needs to be escaped twice instead \\'
Boolean Types
bool represents boolean types
Either true or false
Zero-Value is false
// Example of Boolean// ------------------fmt.Println("Example of Boolean:")
fmt.Println("-------------------")
// Declaration: Default to falsevarflagbool// Declaration and initializationisAdult:=truefmt.Println("flag =", flag)
fmt.Println("isAdult =", isAdult)
Numeric Types
12 Numeric Types grouped into 3 categories
Some are used more often than others
Also a Complex Type: complex
8 Integers
Signed
Unsigned
int8
uint8 / alias: byte
int16
uint16
int32 / alias: int
uint32 / alias: uint
int64 / alias: int
uint64 / alias: uint
2 Special Integers
rune
uintptr
2 Floating-Points
float32
float64
Integer Types
Both signed and unsigned integer types
From 1 byte to 8 bytes in length
Zero-Value = 0
Literal Default Type: int
Integer Type
Range
int8
...
int16
...
int32
...
int64
...
uint8
...
uint16
...
uint32
...
uint64
...
Special Integer Types
byte is alias for uint8
int is alias to:
int32 on 32-bit system
int64 on 64-bit system
uint is alias to:
uint32 on 32-bit system
uint64 on 64-bit system
NOTE: Some 64-bit architectures use int32 as int and uint32 as uint
amd64p32
mips64p32
mips64p321e
Comparing or operating between values of type (int and int32/int64) or (uint and uint32/uint64) is a compile-time error
Because int and uint are not consistent across platforms
Explicit type-conversion is necessary to do so
NOTE: rune and uintptr are also Special Integer Types
Which Integer Type To Use
Follow 3 simple rules
When working on binary file or networking protocols, if integer size is specified, use equivalent size
When writing library function that should work on any integer type, use Generics to represent any integer types
In all other cases, use int
NOTE: Some legacy codes might define 2 separate functions using int64 and uint64
Those APIs were created before Go started supporting Generics
Complex Number can be used for Mandelbrot or Quadratic Equation solver
Strings and Runes
String is a built-in data type
Supports Unicode
We can put any unicode characters in strings
But we should not just add anything just because we can
Zero-Value is ""
Default Type is string
Strings are immutable
String Operators
Type
Operator
Comparison Operator
==!=
Ordering Operators
>>=<<=
Concatenation
++=
Runes
Represent a single Unicode Codepoint
Used for representing a character
Alias for int32 type
Characters are represented as integer codepoints
But we use rune for character, not int32
They are same for the compiler, but bad programming practice to mismatch
Helps to clarify the intent of the code
Default type is rune
// GoodvarfNameInitialrune='M'// Bad - Legal but confusingvarlNameInitialint32='R'
Explicit Type Conversion Required
Most languages have Automatic Type Promotions/Upgrades
E.g. bool -> int -> float -> string
But the rule to apply this can get complicated and lead to unexpected results
Go does not allow Automatic Type Promotions
Type conversions must be explicit casts
Even for integers and floats
Type conversion is done by calling the type as a function on the value
For any values that are not of the same type: At least one must be converted before they can be used together
NOTE: The same applies for values of same type but different sizes (E.g. int8 and int32)
// Example of Type Conversion// --------------------------fmt.Println("Example of Type Conversion:")
fmt.Println("--------------------------")
varmyIntint=10varmyFloatfloat64=30.2// Explicit type conversion is requiredvarmySumFfloat64=float64(myInt) +myFloatvarmySumIint=myInt+int(myFloat)
fmt.Printf("%d + %.1f\n", myInt, myFloat)
fmt.Println("mySumF =", mySumF)
fmt.Println("mySumI =", mySumI)
Due to this strictness, we cannot treat other types as booleans either
In other languages, some values are treated as truthy or falsy
Go does not allow truthy and falsy values
No other types can be converted to booleans, either implicitly or explicitly
Must always use comparison operators to get back booleans
E.g. A typical check that is used a lot is x != nil
Literals Are Untyped
We cannot add 2 integers of different types
But we can use integer literals as floating-point
This is because literals are untyped
Their types are not enforced until needed
This allows literals to be used with any compatible variables
varxfloat64=100varyfloat64=200.50*50
However, this only works on compatible/similar types
We cannot assign strings to numeric variables
Size limitations also exist
Compile-time error if the value overflows the variable type
var vs :=
Go has different ways for declaring variables
Each style communicates about how the variable is used
Variables can be declared at the package-level or function-level
However, it is preferable to do so at the function-level as much as possible
Keep usage of package-level variables to a minimum
Long-Format Declaration With var
This is the most verbose format
Most general case
// Long-format declaration with varvarxint=100
Short-Format Declaration With var
If the type of the value can be inferred from a given value, we can skip specifying the type
The type of the variable is inferred from the assigned value
However, literals are untyped until used
The type of the variable is assigned as the default type of the literal
// Short-format declaration with varvarx=100// Inferred as int (default type of this literal)
Declaration-Only With var
We can declare only, without assigning an initial value
The type is required
Assigns the Zero-Value of the type as the default value
// Declaration-Only with varvarxint// Default Zero-Value of int: 0
Multiple Declaration
This is similar to Python's tuple assignment
The variables can be of the same type
// Multiple declaration and assignmentvarage, favNumint=26, 100
We can also declare-only and assign Zero-Value
// Multiple declaration onlyvarage, favNumint
If assigning initial values, the variables can be of different types
The types of the variables are deduced from the assigned values
However, literals are untyped until used
The type of the variable is assigned as the default type of the literal
// Declaring and assigning multiplesvarname, age, isAdult="John", 26, true
We can also declare a list of variables at once using ()
This approach allows some to have initial values and some not
We can combine different previous approach
var (
uFname, uLnamestring// Default Zero-Value: ""uAge=26// Default type of 26: intuIsAdultbool=trueuFavNum, uWorstNum=7, -100// Default types: int
)
Walrus Shortcut :=
:= can replace var declaration with type inference
:= is typically preferred over var
But it can only be used inside functions
However, it can get confusing if the inferred type is not obvious
// Equivalent statementsvarnum=100num:=100
We can also use it with multiple variables at once
// Using := With Multiple Variablesname, age, isAdult:="John", 26, true
NOTE: := can only be used for declaration, not for re-assignments
However, with multiple declarations, it can be used for re-assignment if at least one variable is new
// Using := With Multiple Variables & Reassignmentname, age:="John", 15name, age, isAdult:="Johnny", 26, true
Limitation of :=
It cannot be used to declare variables at the package-level
var must be used for package-level variables
:= can only be used inside functions
Which Style To Choose
Choose what makes the intent clear
var is more explicit but can be verbose
:= is easy to understand but can be confusing if the value is not explicit (e.g. return from function call)
var is the only option for package-level variables
:= is the most common inside functions
Use declaration lists when declaring multiple variables
Avoid := when:
Declaring package-level variables
Initializing to zero-value
Assigning an untyped constant
Variable type cannot/should not be deduced from the assigned value
Prefer var x byte = 20 over x := byte(20)
Sometimes, := creates a new variable than using an existing one (Shadowing)
In those cases, it is better to use var to be explicit
NOTE: Only use Multiple declaration and assignment style when assigning multiple values from a function return
NOTE: Rarely declare variables outside functions
Package-level variable is not a good idea
Can be difficult to track the changes made
Make it hard to understand the flow of the data in the program
Can lead to subtle bugs
Only declare them as immutable
Using const
Allows to declare values as immutable (at compile-time, not runtime)
Usage is very similar to var
But re-assignments result in error
// Example of Package-level Constants// ----------------------------------constpifloat64=3.1415const (
idKey="id"nameKey="name"
)
consttotal=20*10// untyped// This is the main entry of the application.funcmain() {
// Example of Function-level Constants// -----------------------------------fmt.Println("Example of Function-level Constants:")
fmt.Println("------------------------------------")
constgreetingsConst="Hello"fmt.Println("greetingsConst =", greetingsConst)
fmt.Println()
// Example of Package-level Constants// ----------------------------------fmt.Println("Example of Package-level Constants:")
fmt.Println("-----------------------------------")
fmt.Println("pi =", pi)
fmt.Println("idKey =", idKey)
fmt.Println("nameKey =", nameKey)
fmt.Println("total =", total)
// This is an error: Constants are immutable// pi = pi + 1// greetingsConst = "Hi!"fmt.Println()
}
NOTE: const in Go is very limited
Can only hold values that the compiler can figure out at compile-time
Can be assigned:
Numeric Literals
true / false
Strings
Runes
Values from calling complex(), real(), image(), len(), cap()
Expression with operator and preceding value
iota
We cannot specify that a value calculated at runtime is immutable
const is only a way to give names to literals
// This is an error: Calculation of total is done at runtime, not compile-time// total is not a constant at compile-timex:=5y:=6consttotal=x+y
NOTE: There are no immutable arrays, slices, maps, structs
We cannot specify that a field in a struct is immutable
But within a function, it is clear if a variable is being modified
So immutability is less important
Go is call-by-value: Prevents modification to passed-parameters
Typed Const vs Untyped Const
Constants can be typed or untyped
Untyped Constant is the same as literal
Has no type on its own
Has default type when type cannot be inferred
Typed Constant can be assigned directly only to a "variable" of the same type
The const variable must have an explicit type declared
Usage depends on the intent
Constants to be used with multiple numeric types => Keep untyped
Untyped Constants give more flexibility
But in some situations, an enforced type is preferrable: E.g. Enumeration with iota
// Example of Untyped Constant// ---------------------------fmt.Println("Example of Untyped Constant:")
fmt.Println("----------------------------")
constuConst=100// Legal usage: Can be assigned to any compatible typesvariint=uConstvarffloat64=uConstvarbbyte=uConstfmt.Println("uConst =", uConst)
fmt.Println("i =", i)
fmt.Println("f =", f)
fmt.Println("b =", b)
// Example of Typed Constant// -------------------------fmt.Println("Example of Typed Constant:")
fmt.Println("--------------------------")
consttConstint64=100// Legal usage:// Can only be assigned to type int64// Assigning to any other type is a compile errorvari64int64=tConstfmt.Println("tConst =", tConst)
fmt.Println("i64 =", i64)
fmt.Println()
Unused Variables
Every locally-declared variables must be used/read at least once
It is a compile-time error if a declared local name is not used at least once
As long as the variable is read once, Go compiler will stop complaining
// This code will compilepackage main
funcmain() {
x:=10x=20fmt.Println("x =", x)
x=30
}
E.g. above: Go will not catch the unused x = 30
x was read once in fmt.Println() above
3rd-party tools can be used to cleanup these
NOTE: This rule does not apply to constants and package-level variables
It is better to avoid package-level variables
Constants in Go are calculated at compile-time
They are excluded from the compiled binary if unused
Cannot have side-effects during runtime
// This code will compile and produce nothingpackage main
varnotestring="It will not complain if I am unused because I am at package-level!"funcmain() {
constPI=10
}
Naming Variables And Constants
Identifier names need to start with a letter or _
Name can contain number, letter, _
Any Unicode character can be used
However, not any unicode character should be used
Name should communicate intent
Should not be difficult to type on keyboard
Look-alike Unicode code-points can be a nightmare
Even if they look alike, they are completely different variables