Go#
Basics#
Command Line Argument Parsing#
package main
import (
// Code to be added here.[1]
"flag"
"fmt"
)
var name string
func init() {
// Code to be added here.[2]
flag.StringVar(&name, "name", "everyone", "The greeting object.")
}
func main() {
// Code to be added here.[3]
flag.Parse()
fmt.Printf("Hello, %s!\n", name)
}
Execution
go run main.go -name="Robert"
Output
Hello, Robert!
Variables#
Definition#
// Variable declaration
var a int = 10
Short Declaration#
// Short declaration
b := 20
fmt.Println(a, b)
Multiple Assignment#
// Multiple assignment
c, d, e := 30, 40, 50
fmt.Println(c, d, e)
Swap Variable Values#
// Swap variable values
f, g := 99, 88
fmt.Println(f, g) // 99 88
f, g = g, f
fmt.Println(f, g) // 88 99
Ignore Values with Underscore#
// Ignore values/return values
h, _ := 10, 20
fmt.Println(h)
Formatted Output#
// Formatted output
fmt.Printf("%d\n", 1) // %d 1 outputs as integer
fmt.Printf("%f\n", 1.1) // %f 1.100000 outputs as float
fmt.Printf("%.2f\n", 1.11111) // %.2f 1.11 outputs as float, keeping 2 decimal places
fmt.Printf("%t\n", false) // %t false outputs as boolean
fmt.Printf("%s\n", "false") // %s false outputs as string
fmt.Printf("%c\n", 'a') // %c 'a' outputs as character
fmt.Printf("%p\n", &a) // %p 0x1400012c008 outputs address as pointer
fmt.Printf("%T\n", a) // %T outputs the type of the variable
Get Input#
// Get user input
fmt.Scan(&a) // Input 88
fmt.Println(a) // Output 88
Characters and Bytes#
var n byte = 'a' // byte can only store a single character
var m rune = '树' // rune is equivalent to char in other languages, stores a single Unicode character/Chinese etc.
fmt.Println(n, m)
Constants#
Definition#
Constants store data that will not change.
// Constant definition
const a = false
iota Enumeration#
Constant declarations can use the iota constant generator for initialization, which is used to generate a set of constants initialized with similar rules, but without writing the initialization expression on each line. Note: In a const declaration statement, on the line where the first declared constant is located, iota will be set to 0, and then incremented by one on each line with a constant declaration.
const (
b = iota
c = iota
d = iota
)
fmt.Println(b, c, d) // 0 1 2
const (
f = iota
g
h
)
fmt.Println(f, g, h) // 0 1 2
If the constants are written on the same line with the same value, changing to a new line will increment.
const (
i = iota
j, k = iota, iota
)
fmt.Println(i, j, k) // 0 1 1
Operators#
Arithmetic Operators#
Operator | Description |
---|---|
+ | Addition |
- | Subtraction |
* | Multiplication |
/ | Division |
% | Modulus |
Note: ++ (increment) and -- (decrement) are separate statements in Go, not operators.
Relational Operators#
Operator | Description |
---|---|
== | Checks if two values are equal, returns True if equal, otherwise returns False. |
!= | Checks if two values are not equal, returns True if not equal, otherwise returns False. |
> | Checks if the left value is greater than the right value, returns True if so, otherwise returns False. |
>= | Checks if the left value is greater than or equal to the right value, returns True if so, otherwise returns False. |
< | Checks if the left value is less than the right value, returns True if so, otherwise returns False. |
<= | Checks if the left value is less than or equal to the right value, returns True if so, otherwise returns False. |
Logical Operators#
Operator | Description |
---|---|
&& | Logical AND operator. True if both operands are True, otherwise False. |
! | Logical NOT operator. True if condition is True, otherwise False. |
Bitwise Operators#
Bitwise operators operate on the binary bits of integers in memory.
Operator | Description |
---|---|
& | Bitwise AND. (1 only if both bits are 1) |
^ | Bitwise XOR. (1 if bits are different) |
<< | Left shift n bits, equivalent to multiplying by 2^n. “a<<b” shifts all bits of a left by b bits, discarding high bits and filling low bits with 0. |
>> | Right shift n bits, equivalent to dividing by 2^n. “a>>b” shifts all bits of a right by b bits. |
Assignment Operators#
Operator | Description |
---|---|
= | Simple assignment operator, assigns the value of an expression to a left value. |
+= | Assign after addition. |
-= | Assign after subtraction. |
*= | Assign after multiplication. |
/= | Assign after division. |
%= | Assign after modulus. |
<<= | Assign after left shift. |
>>= | Assign after right shift. |
&= | Assign after bitwise AND. |
= | |
^= | Assign after bitwise XOR. |
Type Conversion#
// Type conversion
c := 3
d := float64(c)
fmt.Println(c, d)
Control Flow#
If Statement (Go does not support ternary)#
• Parentheses around the condition expression can be omitted.
• With initialization statement, can define local variables in the code block.
• The left brace must be at the end of the condition expression.
if booleanExpression {
/* Execute when boolean expression is true */
}
You can declare variables in the expression.
x := 0
if n := "abc"; x > 0 { // The initialization statement does not have to be a variable definition, println("init") is also acceptable.
println(n[2])
} else if x < 0 { // Note the position of the left brace for else if and else.
println(n[1])
} else {
println(n[0])
}
Switch Statement#
Syntax#
Go defaults to having Break, if Break is not needed, you can use the fallthrough keyword.
switch var1 {
case val1:
...
case val2:
...
default:
...
}
The variable var1 can be of any type, while val1 and val2 can be any values of the same type. The type is not limited to constants or integers, but must be of the same type; or the final result is an expression of the same type. You can test multiple possible values that meet the conditions, separated by commas, for example: case val1, val2, val3.
package main
import "fmt"
func main() {
/* Define local variable */
var grade string = "B"
var marks int = 90
switch marks {
case 90: grade = "A"
case 80: grade = "B"
case 50,60,70 : grade = "C"
default: grade = "D"
}
switch {
case grade == "A" :
fmt.Printf("Excellent!\n" )
case grade == "B", grade == "C" :
fmt.Printf("Good\n" )
case grade == "D" :
fmt.Printf("Pass\n" )
case grade == "F":
fmt.Printf("Fail\n" )
default:
fmt.Printf("Poor\n" )
}
fmt.Printf("Your grade is %s\n", grade )
}
The execution result of the above code is:
Excellent!
Your grade is A
Type Switch#
switch x.(type){
case type:
statement(s)
case type:
statement(s)
/* You can define any number of cases */
default: /* Optional */
statement(s)
}
Example#
package main
import "fmt"
func main() {
var x interface{}
// Method 1:
switch i := x.(type) { // With initialization statement
case nil:
fmt.Printf(" x's type :%T\r\n", i)
case int:
fmt.Printf("x is int type")
case float64:
fmt.Printf("x is float64 type")
case func(int) float64:
fmt.Printf("x is func(int) type")
case bool, string:
fmt.Printf("x is bool or string type")
default:
fmt.Printf("Unknown type")
}
// Method 2
var j = 0
switch j {
case 0:
case 1:
fmt.Println("1")
case 2:
fmt.Println("2")
default:
fmt.Println("def")
}
// Method 3
var k = 0
switch k {
case 0:
println("fallthrough")
fallthrough
/*
Go's switch is very flexible, the expression does not have to be a constant or integer, the execution process goes from top to bottom until a match is found;
and if the switch has no expression, it will match true.
In Go, switch is equivalent to each case having a break at the end,
after a successful match, it will not automatically execute other cases below, but jump out of the entire switch,
but you can use fallthrough to force the execution of the following case code.
*/
case 1:
fmt.Println("1")
case 2:
fmt.Println("2")
default:
fmt.Println("def")
}
// Method 4
var m = 0
switch { // Omit condition expression, can be treated as if...else if...else
case m > 0 && m < 10:
fmt.Println("i > 0 and i < 10")
case m > 10 && m < 20:
fmt.Println("i > 10 and i < 20")
default:
fmt.Println("def")
}
}
The execution result of the above code is:
x's type :<nil>
fallthrough
1
1
def
For Statement#
The for loop is a loop control structure that can execute a specified number of loops.
Syntax#
Go's for loop has three forms, only one of which uses a semicolon.
for init; condition; post { }
for condition { }
for { }
init: Generally an assignment expression, assigning an initial value to the control variable;
condition: A relational expression or logical expression, the loop control condition;
post: Generally an assignment expression, incrementing or decrementing the control variable.
The execution process of the for statement is as follows:
① First assign the initial value to the expression init;
② Determine whether the assignment expression init meets the given condition condition, if its value is true, satisfying the loop condition, then execute the statements in the loop body, then execute post, enter the second loop, and determine condition again; otherwise, if the value of condition is false, it does not satisfy the condition, the for loop terminates, and the statements outside the loop body are executed.
s := "abc"
for i, n := 0, len(s); i < n; i++ { // A common for loop, supports initialization statements.
println(s[i])
}
n := len(s)
for n > 0 { // Replace while (n > 0) {}
n--
println(s[n]) // Replace for (; n > 0;) {}
}
for { // Replace while (true) {}
println(s) // Replace for (;;) {}
}
Do not expect the compiler to understand your thoughts; it is a good idea to calculate all results in the initialization statement.
package main
func length(s string) int {
println("call length.")
return len(s)
}
func main() {
s := "abcd"
for i, n := 0, length(s); i < n; i++ { // Avoid multiple calls to the length function.
println(i, s[i])
}
}
Output:
call length.
0 97
1 98
2 99
3 100
Example#
package main
import "fmt"
func main() {
var b int = 15
var a int
numbers := [6]int{1, 2, 3, 5}
/* for loop */
for a := 0; a < 10; a++ {
fmt.Printf("a's value is: %d\n", a)
}
for a < b {
a++
fmt.Printf("a's value is: %d\n", a)
}
for i,x:= range numbers {
fmt.Printf("The %d position x's value = %d\n", i,x)
}
}
The output of the above example is:
a's value is: 0
a's value is: 1
a's value is: 2
a's value is: 3
a's value is: 4
a's value is: 5
a's value is: 6
a's value is: 7
a's value is: 8
a's value is: 9
a's value is: 1
a's value is: 2
a's value is: 3
a's value is: 4
a's value is: 5
a's value is: 6
a's value is: 7
a's value is: 8
a's value is: 9
a's value is: 10
a's value is: 11
a's value is: 12
a's value is: 13
a's value is: 14
a's value is: 15
The 0 position x's value = 1
The 1 position x's value = 2
The 2 position x's value = 3
The 3 position x's value = 5
The 4 position x's value = 0
The 5 position x's value = 0
Nested Loops#
A for loop can nest one or more for loops.
Syntax
The following is the format of nested loops in Go:
for [condition | ( init; condition; increment ) | Range]
{
for [condition | ( init; condition; increment ) | Range]
{
statement(s)
}
statement(s)
}
Example
The following example uses nested loops to output prime numbers between 2 and 100:
package main
import "fmt"
func main() {
/* Define local variables */
var i, j int
for i=2; i < 100; i++ {
for j=2; j <= (i/j); j++ {
if(i%j==0) {
break // If a factor is found, it is not a prime number
}
}
if(j > (i/j)) {
fmt.Printf("%d is a prime number\n", i)
}
}
}
The output of the above example is:
2 is a prime number
3 is a prime number
5 is a prime number
7 is a prime number
11 is a prime number
13 is a prime number
17 is a prime number
19 is a prime number
23 is a prime number
29 is a prime number
31 is a prime number
37 is a prime number
41 is a prime number
43 is a prime number
47 is a prime number
53 is a prime number
59 is a prime number
61 is a prime number
67 is a prime number
71 is a prime number
73 is a prime number
79 is a prime number
83 is a prime number
89 is a prime number
97 is a prime number
Infinite Loops#
If the condition statement in the loop is never false, it will result in an infinite loop. We can execute an infinite loop in the for loop statement by only setting a condition expression:
package main
import "fmt"
func main() {
for true {
fmt.Printf("This is an infinite loop.\n");
}
}
Range Statement#
Syntax#
Golang range is similar to iterator operations, returning (index, value) or (key, value).
The range format of the for loop can iterate over slices, maps, arrays, strings, etc. The format is as follows:
for key, value := range oldMap {
newMap[key] = value
}
1st value | 2nd value | ||
---|---|---|---|
string | index | s[index] | unicode, rune |
array/slice | index | s[index] | |
map | key | m[key] | |
channel | element |
You can ignore unwanted return values, or use the special variable "_" for that.
package main
func main() {
s := "abc"
// Ignore 2nd value, supports string/array/slice/map.
for i := range s {
println(s[i])
}
// Ignore index.
for _, c := range s {
println(c)
}
// Ignore all return values, just iterate.
for range s {
}
m := map[string]int{"a": 1, "b": 2}
// Return (key, value).
for k, v := range m {
println(k, v)
}
}
Output:
97
98
99
97
98
99
a 1
b 2
Important Note#
*
Note that range will copy objects.
package main
import "fmt"
func main() {
a := [3]int{0, 1, 2}
for i, v := range a { // index and value are taken from copies.
if i == 0 { // Before modifying, we first modify the original array.
a[1], a[2] = 999, 999
fmt.Println(a) // Confirm modification is effective, output [0, 999, 999].
}
a[i] = v + 100 // Modify the original array using the value taken from the copy.
}
fmt.Println(a) // Output [100, 101, 102].
}
Output:
[0 999 999]
[100 101 102]
It is recommended to use reference types, as their underlying data will not be copied.
package main
func main() {
s := []int{1, 2, 3, 4, 5}
for i, v := range s { // Copy struct slice { pointer, len, cap }.
if i == 0 {
s = s[:3] // Modifying the slice will not affect range.
s[2] = 100 // Modifying the underlying data.
}
println(i, v)
}
}
Output:
0 1
1 2
2 100
3 4
4 5
The other two reference types, map and channel, are pointer wrappers, unlike slices which are structs.
Difference Between for and range#
The main difference is in the usage scenarios.
for can iterate over arrays and slices || iterate over maps with integer keys incrementing || iterate over strings.
for range can accomplish everything for can do, but can also do things that for cannot do, including:
Iterate over maps with string type keys while simultaneously getting both key and value || iterate over channels.
Functions#
Definition#
Function Characteristics#
• No need to declare a prototype.
• Supports variable parameters.
• Supports multiple return values.
• Supports named return parameters.
• Supports anonymous functions and closures.
• Functions are also a type, a function can be assigned to a variable.
• Does not support nested functions (a package cannot have two functions with the same name).
• Does not support overloading.
• Does not support default parameters.
Function Declaration#
A function declaration includes a function name, parameter list, return value list, and function body. If a function has no return value, the return list can be omitted. The function starts executing from the first statement until it executes the return statement or the last statement of the function.
A function can have no parameters or accept multiple parameters.
Note that the type comes after the variable name.
When two or more consecutive function named parameters are of the same type, all but the last type can be omitted.
A function can return any number of return values.
Use the keyword func to define a function, and the left brace cannot start on a new line.
func test(x, y int, s string) (int, string) {
// Parameters of the same type can be merged. Multiple return values must use parentheses.
n := x + y
return n, fmt.Sprintf(s, n)
}
Functions are first-class objects, and can be passed as parameters. It is recommended to define complex signatures as function types for better readability.
package main
import "fmt"
func test(fn func() int) int {
return fn()
}
// Define function type.
type FormatFunc func(s string, x, y int) string
func format(fn FormatFunc, s string, x, y int) string {
return fn(s, x, y)
}
func main() {
s1 := test(func() int { return 100 }) // Directly passing an anonymous function as a parameter.
s2 := format(func(s string, x, y int) string {
return fmt.Sprintf(s, x, y)
}, "%d, %d", 10, 20)
println(s1, s2)
}
Output:
100 10, 20
A function with return values must have a clear termination statement; otherwise, it will raise a compilation error.
Parameters#
Regular Parameters#
When a function is defined with parameters, those variables can be called formal parameters of the function. Formal parameters are like local variables defined within the function body.
However, when calling the function, the variables passed in are called actual parameters, and the function can pass parameters in two ways:
Value Passing
This means that when calling the function, an actual parameter is copied and passed into the function, so if the parameter is modified in the function, it will not affect the actual parameter.
func swap(x, y int) int {
... ...
}
Reference Passing
This means that when calling the function, the address of the actual parameter is passed into the function, so any modifications to the parameter in the function will affect the actual parameter.
package main
import (
"fmt"
)
/* Define a function to swap values */
func swap(x, y *int) {
var temp int
temp = *x /* Save the value of x */
*x = *y /* Assign the value of y to x */
*y = temp /* Assign the temp value to y*/
}
func main() {
var a, b int = 1, 2
/*
Call swap() function
&a points to a pointer, the address of variable a
&b points to b pointer, the address of variable b
*/
swap(&a, &b)
fmt.Println(a, b)
}
Output:
2 1
By default, Go uses value passing, meaning that during the call, it does not affect the actual parameters.
Note 1
Whether by value passing or reference passing, what is passed to the function is a copy of the variable, but value passing is a copy of the value. Reference passing is a copy of the address, and generally speaking, address copying is more efficient. Value copying depends on the size of the copied object; the larger the object, the lower the performance.
Note 2
Maps, slices, channels, pointers, and interfaces are passed by reference by default.
Variable Parameters#
Variable parameters mean that the function's parameters are not fixed, and the type behind is fixed. (Variable parameters)
Golang's variable parameters are essentially slices. There can only be one, and it must be the last one.
When assigning parameters, you can pass an array or slice directly without assigning them one by one, and it is especially important to add "..." after the parameters.
func main() {
// Variable parameter function
test(1, 2, 3, 4)// [1 2 3 4]
}
func test(args ...int) {
fmt.Println(args)
}
Method to Pass Slice to Variable Parameters
When using a slice object as a variable parameter, it must be expanded. slice...
package main
import (
"fmt"
)
func test(s string, n ...int) string {
var x int
for _, i := range n {
x += i
}
return fmt.Sprintf(s, x)
}
func main() {
s := []int{1, 2, 3}
res := test("sum: %d", s...) // slice... expand slice
println(res)
}
Return Values#
The identifier "_" is used to ignore a certain return value of a function.
Go's return values can be named, and can be used just like variables declared at the beginning of the function body.
The names of return values should have some meaning and can be used as documentation.
A return statement with no parameters returns the current values of each return variable. This usage is called "naked" return.
Direct return statements should only be used in short functions like the one below. In longer functions, they will affect the readability of the code.
package main
import (
"fmt"
)
func add(a, b int) (c int) {
c = a + b
return
}
func calc(a, b int) (sum int, avg int) {
sum = a + b
avg = (a + b) / 2
return
}
func main() {
var a, b int = 1, 2
c := add(a, b)
sum, avg := calc(a, b)
fmt.Println(a, b, c, sum, avg)
}
Output:
1 2 3 3 1
Golang return values cannot be received using container objects. They can only be received using multiple variables or ignored with "_".
package main
func test() (int, int) {
return 1, 2
}
func main() {
// s := make([]int, 2)
// s = test() // Error: multiple-value test() in single-value context
x, _ := test()
println(x)
}
Output:
1
Multiple return values can be directly used as actual parameters for other function calls.
package main
func test() (int, int) {
return 1, 2
}
func add(x, y int) int {
return x + y
}
func sum(n ...int) int {
var x int
for _, i := range n {
x += i
}
return x
}
func main() {
println(add(test()))
println(sum(test()))
}
Output:
3
3
Named return parameters can be seen as local variables similar to formal parameters, and are implicitly returned by the last return statement.
package main
func add(x, y int) (z int) {
z = x + y
return
}
func main() {
println(add(1, 2))
}
Output:
3
Named return parameters can be shadowed by local variables with the same name, in which case an explicit return is required.
func add(x, y int) (z int) {
{ // Cannot be at the same level, causing "z redeclared in this block" error.
var z = x + y
// return // Error: z is shadowed during return
return z // Must return explicitly.
}
}
Named return parameters allow deferred calls to read and modify through closures.
package main
func add(x, y int) (z int) {
defer func() {
z += 100
}()
z = x + y
return
}
func main() {
println(add(1, 2))
}
Output:
103
Explicit return before returning will first modify the named return parameter.
package main
func add(x, y int) (z int) {
defer func() {
println(z) // Output: 203
}()
z = x + y
return z + 200 // Execution order: (z = z + 200) -> (call defer) -> (return)
}
func main() {
println(add(1, 2)) // Output: 203
}
Output:
203
203
Anonymous Functions#
Anonymous functions are functions that do not require a function name. LISP first adopted anonymous functions in 1958.
In Go, functions can be defined in code like ordinary variables, and Go supports defining anonymous functions at any time in the code.
Anonymous functions consist of a function declaration without a function name and a function body. The advantage of anonymous functions is that they can directly use variables within the function without declaring them.
package main
import (
"fmt"
"math"
)
func main() {
getSqrt := func(a float64) float64 {
return math.Sqrt(a)
}
fmt.Println(getSqrt(4))
}
Output:
2
Closures#
Because functions are destroyed after they are called, the variable values inside the function are not saved.
func test1(a int) {
a++
fmt.Println(a)
}
func main() {
a := 1
for i := 0; i < 10; i++ {
test1(a)
}
}
Output
2
2
2
2
2
2
2
2
2
2
The return value is also a function, which is called a closure.
You can achieve persistence of functions in the stack area through anonymous functions and closures.
func main(){
a := 1
f := test2(a)
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
func test2(a int) func() int {
return func() int {
return a
}
}
Output result
2
3
4
5
6
7
8
9
10
11
Recursive Functions#
Recursion is when a function calls itself during execution. A function that calls itself is called a recursive function.
Conditions for recursion:
1. The subproblem must be the same as the original problem, but simpler.
2. It cannot call itself indefinitely; it must have an exit point to simplify to a non-recursive situation.
Factorial#
package main
import "fmt"
func factorial(i int) int {
if i <= 1 {
return 1
}
return i * factorial(i-1)
}
func main() {
var i int = 7
fmt.Printf("Factorial of %d is %d\n", i, factorial(i))
}
Output:
Factorial of 7 is 5040
Fibonacci Sequence#
This sequence starts from the third term, where each term equals the sum of the previous two terms.
package main
import "fmt"
func fibonaci(i int) int {
if i == 0 {
return 0
}
if i == 1 {
return 1
}
return fibonaci(i-1) + fibonaci(i-2)
}
func main() {
var i int
for i = 0; i < 10; i++ {
fmt.Printf("%d\n", fibonaci(i))
}
}
Output:
0
1
1
2
3
5
8
13
21
34
Defer Delayed Calls#
Defer Characteristics#
1. The defer keyword is used to register delayed calls.
2. These calls are executed just before return. Therefore, they can be used for resource cleanup.
3. Multiple defer statements are executed in a last-in-first-out order.
4. The variables in the defer statement are determined at the time of the defer declaration.
Defer Usage#
1. Close file handles
2. Release resource locks
3. Release database connections
Defer is last-in-first-out.
package main
import "fmt"
func main() {
var whatever [5]struct{}
for i := range whatever {
defer fmt.Println(i)
}
}
Output:
4
3
2
1
0
Defer and Closures#
Defer will delay the execution of the function; although the anonymous function is called immediately, it will not execute until the entire main() function ends.
Since the anonymous function is prefixed with defer, it will not execute immediately. However, the problem is that the program starts executing from the top, and when it reaches the anonymous function, although it does not execute immediately, the parameter passing is already completed.
Defer and Exceptions#
Multiple deferred registrations are executed in a FILO order (first in, last out). Even if a function or a delayed call encounters an error, these calls will still be executed.
package main
func test(x int) {
defer println("a")
defer println("b")
defer func() {
println(100 / x) // div0 exception not caught, gradually passed out, ultimately terminating the process.
}()
defer println("c")
}
func main() {
test(0)
}
Output:
c
b
a
panic: runtime error: integer divide by zero
Important#
*
Note that the parameters in the deferred call are evaluated or copied at the time of registration; you can use pointers or closures to "defer" read.
package main
func test() {
x, y := 10, 20
defer func(i int) {
println("defer:", i, y) // y closure reference
}(x) // x is copied <<<<<------ here the closure passes the parameter in
x += 10
y += 100
println("x =", x, "y =", y)
}
func main() {
test()
}
Output:
x = 20 y = 120
defer: 10 120
Misuse of Defer#
*
Misusing defer may lead to performance issues, especially in a "large loop".
package main
import (
"fmt"
"sync"
"time"
)
var lock sync.Mutex
func test() {
lock.Lock()
lock.Unlock()
}
func testdefer() {
lock.Lock()
defer lock.Unlock()
}
func main() {
func() {
t1 := time.Now()
for i := 0; i < 10000; i++ {
test()
}
elapsed := time.Since(t1)
fmt.Println("test elapsed: ", elapsed)
}()
func() {
t1 := time.Now()
for i := 0; i < 10000; i++ {
testdefer()
}
elapsed := time.Since(t1)
fmt.Println("testdefer elapsed: ", elapsed)
}()
}
Output:
test elapsed: 223.162µs
testdefer elapsed: 781.304µs
Defer Traps#
Defer and Closure
package main
import (
"errors"
"fmt"
)
func foo(a, b int) (i int, err error) {
defer fmt.Printf("first defer err %v\n", err)
defer func(err error) { fmt.Printf("second defer err %v\n", err) }(err)
defer func() { fmt.Printf("third defer err %v\n", err) }()
if b == 0 {
err = errors.New("divided by zero!")
return
}
i = a / b
return
}
func main() {
foo(2, 0)
}
Output:
third defer err divided by zero!
second defer err <nil>
first defer err <nil>
Explanation: If the defer following is not a closure, the last execution will not yield the latest value.
Defer and Return
package main
import "fmt"
func foo() (i int) {
i = 0
defer func() {
fmt.Println(i)
}()
return 2
}
func main() {
foo()
}
Output:
2
Explanation: In a function with named return values (here the named return value is i), when executing return 2, i's value is actually reassigned to 2. So the defer closure outputs the result as 2 instead of 1.
Defer Nil Function
package main
import (
"fmt"
)
func test() {
var run func() = nil
defer run()
fmt.Println("runs")
}
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
test()
}
Output:
runs
runtime error: invalid memory address or nil pointer dereference
Explanation: The function named test runs to completion, then the defer function will be executed and panic due to the value being nil. However, it is worth noting that the declaration of run() is fine because it will only be called after the test function runs to completion.
Arrays#
1. Arrays: A fixed-length sequence of the same data type.
2. Array definition: var a [len]int, for example: var a [5]int, the length of the array must be a constant and is part of the type. Once defined, the length cannot change.
3. The length is part of the array type, so var a[5] int and var a[10]int are different types.
4. Arrays can be accessed via subscripts, with subscripts starting from 0, the last element's subscript is: len-1
for i := 0; i < len(a); i++ {
}
for index, v := range a {
}
5. Out-of-bounds access will trigger a panic if the subscript is outside the valid range of the array.
6. Arrays are value types, assignment and parameter passing will copy the entire array, not the pointer. Therefore, changing the value of the copy will not change the value of the original.
7. Supports "==" and "!=" operators, as memory is always initialized.
8. Pointer array [n]*T, array pointer *[n]T.
Array Initialization#
One-Dimensional Array#
Global:
var arr0 [5]int = [5]int{1, 2, 3}
var arr1 = [5]int{1, 2, 3, 4, 5}
var arr2 = [...]int{1, 2, 3, 4, 5, 6}
var str = [5]string{3: "hello world", 4: "tom"}
Local:
a := [3]int{1, 2} // Uninitialized elements default to 0.
b := [...]int{1, 2, 3, 4} // Determine array length by initialization values.
c := [5]int{2: 100, 4: 200} // Initialize elements using index.
d := [...]struct {
name string
age uint8
}{
{"user1", 10}, // Element type can be omitted.
{"user2", 20}, // Don't forget the last comma.
}
Code:
package main
import (
"fmt"
)
var arr0 [5]int = [5]int{1, 2, 3}
var arr1 = [5]int{1, 2, 3, 4, 5}
var arr2 = [...]int{1, 2, 3, 4, 5, 6}
var str = [5]string{3: "hello world", 4: "tom"}
func main() {
a := [3]int{1, 2} // Uninitialized elements default to 0.
b := [...]int{1, 2, 3, 4} // Determine array length by initialization values.
c := [5]int{2: 100, 4: 200} // Initialize elements using index.
d := [...]struct {
name string
age uint8
}{
{"user1", 10}, // Element type can be omitted.
{"user2", 20}, // Don't forget the last comma.
}
fmt.Println(arr0, arr1, arr2, str)
fmt.Println(a, b, c, d)
}
Output:
[1 2 3 0 0] [1 2 3 4 5] [1 2 3 4 5 6] [ hello world tom]
[1 2 0] [1 2 3 4] [0 0 100 0 200] [{user1 10} {user2 20}]
Multi-Dimensional Array#
Global
var arr0 [5][3]int
var arr1 [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
Local:
a := [2][3]int{{1, 2, 3}, {4, 5, 6}}
b := [...][2]int{{1, 1}, {2, 2}, {3, 3}} // The 2nd dimension cannot use "..." .
Code:
package main
import (
"fmt"
)
var arr0 [5][3]int
var arr1 [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
func main() {
a := [2][3]int{{1, 2, 3}, {4, 5, 6}}
b := [...][2]int{{1, 1}, {2, 2}, {3, 3}} // The 2nd dimension cannot use "..." .
fmt.Println(arr0, arr1)
fmt.Println(a, b)
}
Output:
[[0 0 0] [0 0 0] [0 0 0] [0 0 0] [0 0 0]] [[1 2 3] [7 8 9]]
[[1 2 3] [4 5 6]] [[1 1] [2 2] [3 3]]
Value copy behavior can cause performance issues, and it is generally recommended to use slices or array pointers.
package main
import (
"fmt"
)
func test(x [2]int) {
fmt.Printf("x: %p\n", &x)
x[1] = 1000
}
func main() {
a := [2]int{}
fmt.Printf("a: %p\n", &a)
test(a)
fmt.Println(a)
}
Slices#
Slice is not an array or an array pointer. It references a segment of an array through internal pointers and related properties to achieve a variable-length scheme.
1. Slice: A slice is a reference type of an array, so slices are reference types. But itself is a struct, value copy passing.
2. The length of a slice can change, so a slice is a mutable array.
3. The way to iterate over a slice is the same as that of an array, and you can use len() to get the length. It indicates the number of available elements, and read/write operations cannot exceed this limit.
4. Cap can be used to find the maximum expansion capacity of a slice, which cannot exceed the array limit. 0 <= len(slice) <= len(array), where array is the array referenced by the slice.
5. Slice definition: var variableName []type, for example var str []string var arr []int.
6. If slice == nil, then both len and cap results are equal to 0.
Slice Creation#
package main
import "fmt"
func main() {
//1. Declare a slice
var s1 []int
if s1 == nil {
fmt.Println("is empty")
} else {
fmt.Println("is not empty")
}
// 2.:=
s2 := []int{}
// 3.make()
var s3 []int = make([]int, 0)
fmt.Println(s1, s2, s3)
// 4. Initialize assignment
var s4 []int = make([]int, 0, 0)
fmt.Println(s4)
s5 := []int{1, 2, 3}
fmt.Println(s5)
// 5. Slice from array
arr := [5]int{1, 2, 3, 4, 5}
var s6 []int
// Front included, back excluded
s6 = arr[1:4]
fmt.Println(s6)
}
Slice Initialization#
// Global:
var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var slice0 []int = arr[start:end]
var slice1 []int = arr[:end]
var slice2 []int = arr[start:]
var slice3 []int = arr[:]
var slice4 = arr[:len(arr)-1] // Remove the last element from the slice
// Local:
arr2 := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
slice5 := arr[start:end]
slice6 := arr[:end]
slice7 := arr[start:]
slice8 := arr[:]
slice9 := arr[:len(arr)-1] // Remove the last element from the slice
Code:
package main
import (
"fmt"
)
var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var slice0 []int = arr[2:8]
var slice1 []int = arr[0:6] // Can be simplified as var slice []int = arr[:end]
var slice2 []int = arr[5:10] // Can be simplified as var slice[]int = arr[start:]
var slice3 []int = arr[0:len(arr)] // var slice []int = arr[:]
var slice4 = arr[:len(arr)-1] // Remove the last element from the slice
func main() {
fmt.Printf("Global variable: arr %v\n", arr)
fmt.Printf("Global variable: slice0 %v\n", slice0)
fmt.Printf("Global variable: slice1 %v\n", slice1)
fmt.Printf("Global variable: slice2 %v\n", slice2)
fmt.Printf("Global variable: slice3 %v\n", slice3)
fmt.Printf("Global variable: slice4 %v\n", slice4)
fmt.Printf("-----------------------------------\n")
arr2 := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
slice5 := arr[2:8]
slice6 := arr[0:6] // Can be simplified as slice := arr[:end]
slice7 := arr[5:10] // Can be simplified as slice := arr[start:]
slice8 := arr[0:len(arr)] // slice := arr[:]
slice9 := arr[:len(arr)-1] // Remove the last element from the slice
fmt.Printf("Local variable: arr2 %v\n", arr2)
fmt.Printf("Local variable: slice5 %v\n", slice5)
fmt.Printf("Local variable: slice6 %v\n", slice6)
fmt.Printf("Local variable: slice7 %v\n", slice7)
fmt.Printf("Local variable: slice8 %v\n", slice8)
fmt.Printf("Local variable: slice9 %v\n", slice9)
}
Output:
Global variable: arr [0 1 2 3 4 5 6 7 8 9]
Global variable: slice0 [2 3 4 5 6 7]
Global variable: slice1 [0 1 2 3 4 5]
Global variable: slice2 [5 6 7 8 9]
Global variable: slice3 [0 1 2 3 4 5 6 7 8 9]
Global variable: slice4 [0 1 2 3 4 5 6 7 8]
-----------------------------------
Local variable: arr2 [9 8 7 6 5 4 3 2 1 0]
Local variable: slice5 [2 3 4 5 6 7]
Local variable: slice6 [0 1 2 3 4 5]
Local variable: slice7 [5 6 7 8 9]
Local variable: slice8 [0 1 2 3 4 5 6 7 8 9]
Local variable: slice9 [0 1 2 3 4 5 6 7 8]
Slices Copy and Pass Parameters#
package main
import "fmt"
func printArr(arr *[5]int) {
arr[0] = 10
for i, v := range arr {
fmt.Println(i, v)
}
}
func main() {
var arr1 [5]int
printArr(&arr1)
fmt.Println(arr1)
arr2 := [...]int{2, 4, 6, 8, 10}
printArr(&arr2)
fmt.Println(arr2)
}
Slices#
Slice is not an array or an array pointer. It references a segment of an array through internal pointers and related properties to achieve a variable-length scheme.
1. Slice: A slice is a reference type of an array, so slices are reference types. But itself is a struct, value copy passing.
2. The length of a slice can change, so a slice is a mutable array.
3. The way to iterate over a slice is the same as that of an array, and you can use len() to get the length. It indicates the number of available elements, and read/write operations cannot exceed this limit.
4. Cap can be used to find the maximum expansion capacity of a slice, which cannot exceed the array limit. 0 <= len(slice) <= len(array), where array is the array referenced by the slice.
5. Slice definition: var variableName []type, for example var str []string var arr []int.
6. If slice == nil, then both len and cap results are equal to 0.
Slice Creation#
package main
import "fmt"
func main() {
//1. Declare a slice
var s1 []int
if s1 == nil {
fmt.Println("is empty")
} else {
fmt.Println("is not empty")
}
// 2.:=
s2 := []int{}
// 3.make()
var s3 []int = make([]int, 0)
fmt.Println(s1, s2, s3)
// 4. Initialize assignment
var s4 []int = make([]int, 0, 0)
fmt.Println(s4)
s5 := []int{1, 2, 3}
fmt.Println(s5)
// 5. Slice from array
arr := [5]int{1, 2, 3, 4, 5}
var s6 []int
// Front included, back excluded
s6 = arr[1:4]
fmt.Println(s6)
}
Slice Initialization#
// Global:
var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var slice0 []int = arr[start:end]
var slice1 []int = arr[:end]
var slice2 []int = arr[start:]
var slice3 []int = arr[:]
var slice4 = arr[:len(arr)-1] // Remove the last element from the slice
// Local:
arr2 := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
slice5 := arr[start:end]
slice6 := arr[:end]
slice7 := arr[start:]
slice8 := arr[:]
slice9 := arr[:len(arr)-1] // Remove the last element from the slice
Code:
package main
import (
"fmt"
)
var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var slice0 []int = arr[2:8]
var slice1 []int = arr[0:6] // Can be simplified as var slice []int = arr[:end]
var slice2 []int = arr[5:10] // Can be simplified as var slice[]int = arr[start:]
var slice3 []int = arr[0:len(arr)] // var slice []int = arr[:]
var slice4 = arr[:len(arr)-1] // Remove the last element from the slice
func main() {
fmt.Printf("Global variable: arr %v\n", arr)
fmt.Printf("Global variable: slice0 %v\n", slice0)
fmt.Printf("Global variable: slice1 %v\n", slice1)
fmt.Printf("Global variable: slice2 %v\n", slice2)
fmt.Printf("Global variable: slice3 %v\n", slice3)
fmt.Printf("Global variable: slice4 %v\n", slice4)
fmt.Printf("-----------------------------------\n")
arr2 := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
slice5 := arr[2:8]
slice6 := arr[0:6] // Can be simplified as slice := arr[:end]
slice7 := arr[5:10] // Can be simplified as slice := arr[start:]
slice8 := arr[0:len(arr)] // slice := arr[:]
slice9 := arr[:len(arr)-1] // Remove the last element from the slice
fmt.Printf("Local variable: arr2 %v\n", arr2)
fmt.Printf("Local variable: slice5 %v\n", slice5)
fmt.Printf("Local variable: slice6 %v\n", slice6)
fmt.Printf("Local variable: slice7 %v\n", slice7)
fmt.Printf("Local variable: slice8 %v\n", slice8)
fmt.Printf("Local variable: slice9 %v\n", slice9)
}
Output:
Global variable: arr [0 1 2 3 4 5 6 7 8 9]
Global variable: slice0 [2 3 4 5 6 7]
Global variable: slice1 [0 1 2 3 4 5]
Global variable: slice2 [5 6 7 8 9]
Global variable: slice3 [0 1 2 3 4 5 6 7 8 9]
Global variable: slice4 [0 1 2 3 4 5 6 7 8]
-----------------------------------
Local variable: arr2 [9 8 7 6 5 4 3 2 1 0]
Local variable: slice5 [2 3 4 5 6 7]
Local variable: slice6 [0 1 2 3 4 5]
Local variable: slice7 [5 6 7 8 9]
Local variable: slice8 [0 1 2 3 4 5 6 7 8 9]
Local variable: slice9 [0 1 2 3 4 5 6 7 8]
Slices Copy and Pass Parameters#
package main
import "fmt"
func printArr(arr *[5]int) {
arr[0] = 10
for i, v := range arr {
fmt.Println(i, v)
}
}
func main() {
var arr1 [5]int
printArr(&arr1)
fmt.Println(arr1)
arr2 := [...]int{2, 4, 6, 8, 10}
printArr(&arr2)
fmt.Println(arr2)
}
Slices#
Slice is not an array or an array pointer. It references a segment of an array through internal pointers and related properties to achieve a variable-length scheme.
1. Slice: A slice is a reference type of an array, so slices are reference types. But itself is a struct, value copy passing.
2. The length of a slice can change, so a slice is a mutable array.
3. The way to iterate over a slice is the same as that of an array, and you can use len() to get the length. It indicates the number of available elements, and read/write operations cannot exceed this limit.
4. Cap can be used to find the maximum expansion capacity of a slice, which cannot exceed the array limit. 0 <= len(slice) <= len(array), where array is the array referenced by the slice.
5. Slice definition: var variableName []type, for example var str []string var arr []int.
6. If slice == nil, then both len and cap results are equal to 0.
Slice Creation#
package main
import "fmt"
func main() {
//1. Declare a slice
var s1 []int
if s1 == nil {
fmt.Println("is empty")
} else {
fmt.Println("is not empty")
}
// 2.:=
s2 := []int{}
// 3.make()
var s3 []int = make([]int, 0)
fmt.Println(s1, s2, s3)
// 4. Initialize assignment
var s4 []int = make([]int, 0, 0)
fmt.Println(s4)
s5 := []int{1, 2, 3}
fmt.Println(s5)
// 5. Slice from array
arr := [5]int{1, 2, 3, 4, 5}
var s6 []int
// Front included, back excluded
s6 = arr[1:4]
fmt.Println(s6)
}
Slice Initialization#
// Global:
var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var slice0 []int = arr[start:end]
var slice1 []int = arr[:end]
var slice2 []int = arr[start:]
var slice3 []int = arr[:]
var slice4 = arr[:len(arr)-1] // Remove the last element from the slice
// Local:
arr2 := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
slice5 := arr[start:end]
slice6 := arr[:end]
slice7 := arr[start:]
slice8 := arr[:]
slice9 := arr[:len(arr)-1] // Remove the last element from the slice
Code:
package main
import (
"fmt"
)
var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var slice0 []int = arr[2:8]
var slice1 []int = arr[0:6] // Can be simplified as var slice []int = arr[:end]
var slice2 []int = arr[5:10] // Can be simplified as var slice[]int = arr[start:]
var slice3 []int = arr[0:len(arr)] // var slice []int = arr[:]
var slice4 = arr[:len(arr)-1] // Remove the last element from the slice
func main() {
fmt.Printf("Global variable: arr %v\n", arr)
fmt.Printf("Global variable: slice0 %v\n", slice0)
fmt.Printf("Global variable: slice1 %v\n", slice1)
fmt.Printf("Global variable: slice2 %v\n", slice2)
fmt.Printf("Global variable: slice3 %v\n", slice3)
fmt.Printf("Global variable: slice4 %v\n", slice4)
fmt.Printf("-----------------------------------\n")
arr2 := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
slice5 := arr[2:8]
slice6 := arr[0:6] // Can be simplified as slice := arr[:end]
slice7 := arr[5:10] // Can be simplified as slice := arr[start:]
slice8 := arr[0:len(arr)] // slice := arr[:]
slice9 := arr[:len(arr)-1] // Remove the last element from the slice
fmt.Printf("Local variable: arr2 %v\n", arr2)
fmt.Printf("Local variable: slice5 %v\n", slice5)
fmt.Printf("Local variable: slice6 %v\n", slice6)
fmt.Printf("Local variable: slice7 %v\n", slice7)
fmt.Printf("Local variable: slice8 %v\n", slice8)
fmt.Printf("Local variable: slice9 %v\n", slice9)
}
Output:
Global variable: arr [0 1 2 3 4 5 6 7 8 9]
Global variable: slice0 [2 3 4 5 6 7]
Global variable: slice1 [0 1 2 3 4 5]
Global variable: slice2 [5 6 7 8 9]
Global variable: slice3 [0 1 2 3 4 5 6 7 8 9]
Global variable: slice4 [0 1 2 3 4 5 6 7 8]
-----------------------------------
Local variable: arr2 [9 8 7 6 5 4 3 2 1 0]
Local variable: slice5 [2 3 4 5 6 7]
Local variable: slice6 [0 1 2 3 4 5]
Local variable: slice7 [5 6 7 8 9]
Local variable: slice8 [0 1 2 3 4 5 6 7 8 9]
Local variable: slice9 [0 1 2 3 4 5 6 7 8]
Slices Copy and Pass Parameters#
package main
import "fmt"
func printArr(arr *[5]int) {
arr[0] = 10
for i, v := range arr {
fmt.Println(i, v)
}
}
func main() {
var arr1 [5]int
printArr(&arr1)
fmt.Println(arr1)
arr2 := [...]int{2, 4, 6, 8, 10}
printArr(&arr2)
fmt.Println(arr2)
}
Slices#
Slice is not an array or an array pointer. It references a segment of an array through internal pointers and related properties to achieve a variable-length scheme.
1. Slice: A slice is a reference type of an array, so slices are reference types. But itself is a struct, value copy passing.
2. The length of a slice can change, so a slice is a mutable array.
3. The way to iterate over a slice is the same as that of an array, and you can use len() to get the length. It indicates the number of available elements, and read/write operations cannot exceed this limit.
4. Cap can be used to find the maximum expansion capacity of a slice, which cannot exceed the array limit. 0 <= len(slice) <= len(array), where array is the array referenced by the slice.
5. Slice definition: var variableName []type, for example var str []string var arr []int.
6. If slice == nil, then both len and cap results are equal to 0.
Slice Creation#
package main
import "fmt"
func main() {
//1. Declare a slice
var s1 []int
if s1 == nil {
fmt.Println("is empty")
} else {
fmt.Println("is not empty")
}
// 2.:=
s2 := []int{}
// 3.make()
var s3 []int = make([]int, 0)
fmt.Println(s1, s2, s3)
// 4. Initialize assignment
var s4 []int = make([]int, 0, 0)
fmt.Println(s4)
s5 := []int{1, 2, 3}
fmt.Println(s5)
// 5. Slice from array
arr := [5]int{1, 2, 3, 4, 5}
var s6 []int
// Front included, back excluded
s6 = arr[1:4]
fmt.Println(s6)
}
Slice Initialization#
// Global:
var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var slice0 []int = arr[start:end]
var slice1 []int = arr[:end]
var slice2 []int = arr[start:]
var slice3 []int = arr[:]
var slice4 = arr[:len(arr)-1] // Remove the last element from the slice
// Local:
arr2 := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
slice5 := arr[start:end]
slice6 := arr[:end]
slice7 := arr[start:]
slice8 := arr[:]
slice9 := arr[:len(arr)-1] // Remove the last element from the slice
Code:
package main
import (
"fmt"
)
var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var slice0 []int = arr[2:8]
var slice1 []int = arr[0:6] // Can be simplified as var slice []int = arr[:end]
var slice2 []int = arr[5:10] // Can be simplified as var slice[]int = arr[start:]
var slice3 []int = arr[0:len(arr)] // var slice []int = arr[:]
var slice4 = arr[:len(arr)-1] // Remove the last element from the slice
func main() {
fmt.Printf("Global variable: arr %v\n", arr)
fmt.Printf("Global variable: slice0 %v\n", slice0)
fmt.Printf("Global variable: slice1 %v\n", slice1)
fmt.Printf("Global variable: slice2 %v\n", slice2)
fmt.Printf("Global variable: slice3 %v\n", slice3)
fmt.Printf("Global variable: slice4 %v\n", slice4)
fmt.Printf("-----------------------------------\n")
arr2 := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
slice5 := arr[2:8]
slice6 := arr[0:6] // Can be simplified as slice := arr[:end]
slice7 := arr[5:10] // Can be simplified as slice := arr[start:]
slice8 := arr[0:len(arr)] // slice := arr[:]
slice9 := arr[:len(arr)-1] // Remove the last element from the slice
fmt.Printf("Local variable: arr2 %v\n", arr2)
fmt.Printf("Local variable: slice5 %v\n", slice5)
fmt.Printf("Local variable: slice6 %v\n", slice6)
fmt.Printf("Local variable: slice7 %v\n", slice7)
fmt.Printf("Local variable: slice8 %v\n", slice8)
fmt.Printf("Local variable: slice9 %v\n", slice9)
}
Output:
Global variable: arr [0 1 2 3 4 5 6 7 8 9]
Global variable: slice0 [2 3 4 5 6 7]
Global variable: slice1 [0 1 2 3 4 5]
Global variable: slice2 [5 6 7 8 9]
Global variable: slice3 [0 1 2 3 4 5 6 7 8 9]
Global variable: slice4 [0 1 2 3 4 5 6 7 8]
-----------------------------------
Local variable: arr2 [9 8 7 6 5 4 3 2 1 0]
Local variable: slice5 [2 3 4 5 6 7]
Local variable: slice6 [0 1 2 3 4 5]
Local variable: slice7 [5 6 7 8 9]
Local variable: slice8 [0 1 2 3 4 5 6 7 8 9]
Local variable: slice9 [0 1 2 3 4 5 6 7 8]