 A slice is a type that references arrays. Each slice value has three components, an address of some array, a number indicating length, and a number indicating capacity. The distinction between length and capacity will make clear shortly. When we create an uninitialized slice variable, the address, length and capacity all start out zero. Attempting to access elements of the slice will result in runtime errors because the slice doesn't have any actual storage. To give the slice a non-zero length and capacity, we use a special built-in function make. The first argument to make is not an expression, but rather the type of slice to create. So in fact, this built-in function isn't really a function, it doesn't take an expression argument, it doesn't take a value, it's a special syntax that takes a type specification. It's not an ordinary kind of function. The second argument specifies the length, and the third argument specifies the capacity. This call to make here will create an array of eight ints somewhere in memory, and then return a new slice value, which references that new array. This assignment here effectively copies that returned slice value to variable a. Now that we have a non-zero length slice, we can use it like an array. However, even though the array pointed to in the slice has an actual capacity of eight, the slice only has a length of five, and so indexes five and above are considered out of bounds. Now if we create a second slice of ints, variable b, we can assign a to b. But be clear that this only copies the slice value, not the array referenced by the slice. Because a and b both reference the same underlying array, accessing indexes through either a or b accesses the same storage. Here assigning eight to index zero of a also effectively assigns eight to index zero of b. Slices with different lengths and capacities are still considered the same type, and so we can assign slice b to slice a here and vice versa. Slices with different underlying types, however, are not considered the same type. This slice of ints is not the same type as a slice of strings, and so we cannot assign one variable to the other. When a function takes a slice parameter, what the parameter receives is a copy of the slice value, not the underlying array. So here when we pass a slice to function foo, the parameter b points to the same underlying array as a. And so this assignment to the slice in foo affects a. The first index of a now has the value negative seven. The built in append function takes a slice and returns a new slice in which values have been added past the length of the original. If the new values fit in the capacity of the slices existing array, the new slice uses the same existing array. If the new values exceed the capacity of the existing array, the new slice gets a new larger array with the values of the original array copied over. Here the slice a has a length of three but a capacity of five. When we append the values seven and eight to a, the array shared by the original slice and the new one now has the values zero zero zero seven and eight. However, slice a still has a length of three. And so if we attempt to access index three of slice a, we get a runtime out of bounds error. If we subsequently append the values 11 and 12 to a, we get a third slice sharing the same underlying array, which now has the values zero zero zero 11 and 12. If instead we were to append three values 11, 12 and 13, the append function will return a slice with a new larger array with the values zero zero zero 11, 12 and 13. When append creates a new larger array, it copies the existing values from the original array. So if we sign 20 to index zero of a before the second append, the new array will have the values 20, zero, zero, 11, 12 and 13. To get the length and capacity of a slice, we use the built-in functions len and cap. Again, an uninitialized slice has a length and capacity of zero. If we append to a zero slice, we get a non-zero slice with the appended values. When append creates a new larger array, it sometimes chooses to make an array larger than is needed for the new values. For example, the new slice created by append here might have a capacity larger than two. Append does this as an optimization. If we append to an array once, we'll likely append to it again, and so allocating extra space may make future appends cheaper if they can use existing arrays rather than creating new ones. When using the subscript operator, the square brackets, we can put inside two numbers separated by a colon. This is the range operator which returns a new slice rather than a single value. Here, a subscript 3 colon 7 returns a new slice which represents indexes 3 up to but not including 7 of the original slice. So this new slice has a length of 4. The capacity of the new slice is 12 because it runs from index 3 to the end of the underlying array, which has a size of 15. Understand that the new slice shares the same underlying array as the original, but the indexes are skewed. Index 0 of the new slice is the same as index 3 of the original. Index 1 of the new slice is the same as index 4 of the original. Index 2 of the new slice is the same as index 5 of the original. And index 3 of the new slice is the same as index 6 of the original. So here, the assignment to index 0 of the new slice effectively modifies index 3 of the original. And assignment to index 2 of the new slice effectively modifies index 5 of the original. We can use this range operator to create new slices from arrays as well as other slices. The new slice here points to the array, but with an offset of 3. If we emit the number on the left side of the colon, it defaults to 0. If we emit the number on the right side of the colon, it defaults to the length of the original slice or array. So if we emit both numbers and just write a colon, we're creating a new slice that covers the whole length. When used on a slice, this is the same as if we just copied the slice by assignment, but for an array, it conveniently gets us a slice representing the array. Like with any other type in the language, we can create arrays of slices. Here we have an array of three slices of strings. The slices all start out as zero slices, so we assign them each a non-zero slice created with make. After these assignments here, the first slice references an array of five strings, the second slice references an array of 80 strings, and the third slice references an array of 30 strings. Just like we can create an array of any type, we can create slices of any type, including slices of arrays. Here we have a slice of arrays of three strings. When we use make to create a slice of arrays of three strings with a capacity of five, the underlying array is then an array of five arrays of three strings. Likewise, if we make a slice of arrays of three arrays of six strings and we give that slice a capacity of five, the slice points to an array of five arrays of three arrays of six strings. In many languages, all local variables are allocated on the call stack rather than the heap. In JavaScript, only closure variables are allocated on the heap because they must live beyond the function calls in which they are created. What's different in Go is that local variables aren't all just references the way they are in JavaScript. An int variable in Go directly stores an int and an array variable directly stores an array. Some Go types, however, like slices, do reference other parts of memory. Therefore, we might encounter situations like this one. The function foo returns a slice which references the local array of ints a. If a were allocated on the stack, the return slice would reference memory that has gone out of scope and all sorts of bad things might happen. The Go compiler avoids this problem by performing escape analysis. When the compiler detects cases where a variable might live beyond the scope in which it gets created, the compiler allocates the variables on the heap rather than the stack. You might ask why the compiler doesn't simply allocate everything on the heap. Well, stack allocations are generally more efficient. The stack doesn't require garbage collection and data on the stack is relatively tightly packed, reducing the need for jumping around in memory and thereby triggering cache misses. Much like JavaScript, Go allows us to create anonymous functions. Here inside the function bar, we create a variable f to be of type funk with two in parameters and returning int. We can then assign to f an anonymous function with that same signature, and having done so, we can then call f as a function. I understand that a function signature, its parameter types and return types, is an integral part of the function's type. Attempting to assign a function to f with a different signature triggers a compilation error. Like with other variable declarations, the type of f can be inferred from its initialization. You might assume that we can declare interfunctions with names as we can in JavaScript, but Go does not allow this. Like in JavaScript, we can immediately invoke anonymous functions. Here the function isn't stored in a variable, but rather just called, and this return value is returned from the outer function. Functions can also be used as return types and parameter types. Here bar returns a function taking two int parameters and returning an int. We assign the returned function to a variable x and then invoke x with the arguments three and seven. If we want to use the returned function only once, we can just directly invoke the returned function without first assigning it to a variable. Like in JavaScript, an interfunction can retain access to local variables from enclosing scopes. Here for example, bar returns a function which retains access to the local variable a of bar. So when we invoke this returned function multiple times, the same variable a is used for each call. If we call bar a second time, we get back a function with the same code, but with a separate variable a. So calls to x and y here are using a different variable a. A variadic function is a function that takes a variable number of arguments. In Go, the last parameter of a function can be a slice especially denoted by three dots instead of square brackets. The arguments for this parameter are zero or more elements which get passed as a slice. Here for example, the last parameter b is a slice of strings denoted with three dots. When calling the function, we pass zero or more strings at the end and these arguments become the values of the slice. If a function returns multiple values, the function can be used to provide arguments to a variadic function if the return values all match the expected type. Here we're calling foo with four strings, the first three returned from the call to bar. Code in Go is organized into namespaces called packages. Each package is known by a name, usually a short single word, and an import path. The import path can be any sequence of characters, but by convention, the import path should be a URL pointing to a git or mercurial repository containing the source files of the package. By convention, the last part of the import path matches the package name. Here for example, we have a package named foo with the import path github.com slash bryan slash foo. When you install the Go tools on your system, you should set an environment variable GoPath to a desired working directory. In the GoPath directory, package source directories live under a directory named src. So for example, if on windows my go path is set to c colon slash go code, then the source files for a package with import path github.com slash bryan slash foo c colon slash go code slash source slash github.com slash bryan slash foo. In a package directory, each source file should end in the extension go, but the compiler otherwise doesn't care what we call the source files. At the top of each source file, we put a package statement naming the package, and after the package statement, we put one or more import statements naming the import paths of the other packages we wish to use in that source file. For reasons of compiling efficiency and imposing clean code structure, Go does not allow package imports to be circular. For example, if package a imports package b, then package b must not import a. When compiling a package, it's more efficient if all the other packages that imports are already separately compiled. Circular imports would create situations where that isn't the case, and so they are not allowed. When using elements from an imported package, we prefix the elements names with their package name and a dot. So if we want to use x from imported package foo, we write foo dot x. Only names starting with an uppercase letter are public, meaning visible to other packages. So anything you want to expose to other packages should begin with an uppercase letter. Here finally is the Hello World program written in Go. A compiled Go program starts execution by invoking the function named main in a package named main. To print a standard output, we use the function print line from the format package, spelled fmt. Notice that the import path of the fmt package is simply fmt. For convenience, the standard library packages have short import paths like this. Your own packages though should follow the convention of using repo URLs for the import paths. At the top level of code, you can only have these statements package import var const funk and type. All other kinds of statements can only be used inside functions. A source files package statement must proceed all other statements, and the imports all come second after the package statement. In general, Go doesn't care about the ordering of the rest of your statements, except in some cases your global var statements, as we'll discuss later. Looking now at how a real world Go program is structured, here's the github page for an NES emulator written in Go. The main package has the import path github.com slash fogelman slash NES, and the two other packages of the program, NES and UI, have import paths that are subdirectories of the main directory. The project also includes one other sub directory for a utility program that can test the validity of ROM files. Because that package is compiled as a separate program, it too must have the name main. Again, understand that the names of the source files beyond the extension dot go does not matter, and how we split our code into separate files within a package also does not matter. Those all are simply matters of stylistic choice. Also understand that each import is visible only in the individual file. If you want to use an imported package in every file of another package, you'll have to import it in each of those files. By default, an imported package is known by its name, but you can choose a different name. This is useful when a package name is too verbose to type or when it conflicts with some other name. Here, the NES package is imported with the conveniently short name N. Each package can have a special function init for doing any setup work which the package needs to do. If the package has a main function, the init function will always run first. When a package is imported by another, the compiler guarantees that the imported package is completely initialized before the local package. Global variables can be initialized by arbitrary expressions, even expressions that use other global variables of the package. Here, for example, the initial value of a depends upon the initial value of b, and the initial value of b depends upon the initial value of c, even though a is declared before b and b is declared before c. The compiler will figure out the appropriate execution order here, so we can write these declarations in any order. Whatever that may be, the variable initializations always run before the optional init function. In some cases, the compiler actually cannot sort out the order of global variable initializations because doing so is logically impossible. Here, for example, the relationship between the variables is circular. a depends upon the value of b, b depends upon the value of c, and c depends upon the value of a. The compiler will first complain that it can't infer the types of these variables, but even if we explicitly declare the types, the compiler will then complain that initialization goes in a loop. Also, watch out for cases where the order of global variable initializations depends upon the order in which they are written in the file. Here, both a and b are initialized by calls to foo, a function which increments and returns global variable c. First, the variable c gets its initial value, then a is initialized by a call to foo, and last b is initialized by a second call to foo. Even though c is initially given the value 2, after the two calls to foo, it will have the value 8.