Primitives
Booleans
Booleans:
- Values are true or false
- NOT an alias for other types (I.E int) unlike in other programming languages
- Zero value is false
// Boolean example
func main() {
n := 1 == 1
m := 1 == 2
fmt.Printf("%v, %T\n", n, n)
fmt.Printf("%v, %T\n", m, m)
}
Numeric Types
Numeric Types:
- Signed Integers:
- int types have varying sizes, but it is always a minimum of 32 bits
- 8 bit (int8) through 64 bit (int64)
- Unsigned integers:
- 8 bit (byte and uint8) through 32 bit (uint32)
- Arithmetic operations:
- Addition, subtraction, multiplication, division, remainder
// Arithmetic example
func main() {
a := 10
b := 3
fmt.Printf(a + b)
fmt.Printf(a - b)
fmt.Printf(a * b)
fmt.Printf(a / b)
fmt.Printf(a % b)
}
Bitwise Operations
Bitwise Operation Types:
- And, or, xor, and not
// Bitwise operations example
func main() {
a := 10 // 1010
b := 3 // 0011
//// And operator
// Bits are set if both numbers has that bit
fmt.Printf(a & b) // 0010 = 2
//// Or operator
// Bits are set if they are in EITHER number
fmt.Printf(a | b) // 1011 = 11
//// Xor operator
// Bits are set if one number has it, BUT NOT both
fmt.Printf(a ^ b) // 1001 = 9
//// And Not operator
// Bits are set if neither one of the numbers has that bit
fmt.Printf(a &^ b) // 0100 = 8
}
// BONUS: Bitshifting example
func main() {
a := 10
// Shifting to the left
fmt.Printf(a << 3) // 2^3 * 2^3 = 64
// Shifting to the right
fmt.Printf(a >> 3) // 2^3 / 2^3 = 2^0
}
Floating Point Numbers
Floating Point Numbers:
- Follows IEEE-754 standard
- Zero value is 0
- 32 and 64 bit versions
- Literal styles:
- Decimal (3.14)
- Exponential (13e18 or 2E10)
- Mixed (13.7e12)
- Arithmetic operations:
- Addition, subtraction, multiplication, division
// Floating point number example
// NOTE: These variables will be initialized as float64
func main() {
n := 3.14
n = 13.7e72
n = 2.1E14
fmt.Printf("%v, %T", n, n)
}
Strings
Text Types:
- Strings:
- UTF-8
- Immutable
- Can be concatenated with plus (+) operator
- Can be converted to []byte
- Rune:
- UTF-32
- Alias for int32
- Special methods normally required to process
- I.E strings.Reader#ReadRune
// String example
func main() {
s := "this is a string"
fmt.Printf("%v, %T\n", string(s[2]), s)
}
Consts & Iota
Constants:
- Immutable, but can be shadowed
- Replaced by the compiler at compile time
- Value must be calculable at compile time
- Named like variables:
- PascalCase for exported constants
- camelCase for internal constants
- Typed constants work like immutable variables
- Can interoperate only with same type
- Untyped constants work like literals
- Can interoperate with similar types
func main() {
const myConst int = 42
fmt.Printf("%v, %T\n", myConst, myConst)
// Const variables can hold all the primitive times
const a int = 14
const b string = "foo"
const c float32 = 3.14
const d bool = true
fmt.Printf("%v\n", a)
fmt.Printf("%v\n", b)
fmt.Printf("%v\n", c)
fmt.Printf("%v\n", d)
}
Enumerated Constants: - Special symbol iota allows related constants to be created easily - Iota starts at 0 in each const block and increments by one - After the first instance of Iota in a const block, you do not need the other variable declarations to say iota - Watch out for constant values that match zero values for variables Enumerated Expressions: - Operations that can be determined at compilation time are allowed: - Arithmetic - Bitwise operations - Bitshifting
//// Basic Iota example
// You can put a iota in a const block too
const (
a = iota
b
c
)
const (
a2 = iota
)
func main() {
// Will print out 0, 1, 2, each on new lines.
fmt.Printf("%v\n", a)
fmt.Printf("%v\n", b)
fmt.Printf("%v\n", c)
// A seperate iota instance
fmt.Printf("%v\n", a2)
}
//// Iota Example #1: Basic specialist
const (
// "_" just means "this is a throwaway, don't use this variable"
_ = iota
catSpecialist
dogSpecialist
snakeSpecialist
)
func main() {
var specialistType int = catSpecialist
fmt.Printf("%v\n", specialistType == catSpecialist)
}
ae
//// Iota Example #2: File size calculus
const (
_ = iota
KB = 1 << (10 * iota)
MB
GB
TB
PB
EB
ZB
YB
)
func main() {
fileSize := 4000000000.
fmt.Printf("%.2fGB\n", fileSize/GB)
}
ae
//// Iota Example #3: Fancy manager key access
const (
isAdmin = 1 << iota
isHeadquarters
canSeeFinancials
canSeeAfrica
canSeeAsia
canSeeEurope
canSeeNorthAmerica
canSeeSouthAmerica
)
func main() {
var roles byte = isAdmin | canSeeFinancials | canSeeEurope
fmt.Printf("%b\n", roles)
fmt.Printf("Is Admin? %v\n", isAdmin & roles == isAdmin)
fmt.Printf("Is HQ? %v\n", isHeadquarters & roles == isHeadquarters)
}
Arrays & Slices
Arrays:
- Collection of items with same type (no mixing types)
- Fixed size
- Declaration styles:
- a := [3]int{1, 2, 3}
- a := [...]int{1, 2, 3}
- var a [3]int
- Accessed via zero-based index
- a := [3]int{1, 3, 5} // a[1] == 3
- len function returns size of array
- Copies refer to different underlying data
Slices:
- Backed by array
- Creation styles:
- Slice existing array or slice
- Literal style
- Via make function
- a := make([]int, 10) // Creates slice with length and capacity == 10
- a := make([]it, 10, 100) // Creates slice with length == 10 and capacity == 100
- "len" function returns length of slice
- "cap" function returns length of underlying capacity
- Use append function to add elements to slices
- May cause expensive copy operation if underlying array is too small
- Copies refer to same underlying array
