Go Fundamentals
A Complete 7-Chapter Course
Table of Contents
- Basics โ package main, go run/build, fmt
- Variables, Basic Types, and := vs var
- Operators, Arithmetic Type Rules, and Control Flow
- Loops: for as Go's Only Loop Keyword
- Functions: Multiple Returns, Named Returns, Variadic Params
- Arrays and Slices
- Maps
Basics โ package main, go run/build, fmt
Go (often called Golang to make it searchable) is a compiled language โ source code is translated into a standalone executable before it runs, rather than being interpreted line-by-line in a browser console. This is the single biggest difference to keep in mind throughout this course: there's no browser, no console.log-and-refresh workflow โ Go code is built, then run, as a real program.
Installing Go and Checking It Works
If that prints a version number, Go is installed and ready to use from the command line.
The Smallest Go Program
Every runnable Go file has exactly these three pieces. package main marks this file as a program (not a reusable library); import "fmt" pulls in the standard formatting/printing package; func main() is the entry point โ execution always starts here, the direct equivalent of the top-level code that ran automatically in every JavaScript example.
fmt.Println(...) prints a line of output to the terminal. It's the most-used function in this entire course, exactly as console.log was throughout the JavaScript course.
Running a Go File
go run compiles the file into a temporary executable and runs it immediately in one step โ the fastest way to try something out. There's also a separate, two-step workflow for producing a permanent program:
go build is what actually happens when shipping a real program โ the resulting file runs on its own, with the Go toolchain itself no longer required on the machine that runs it.
Comments
Comment syntax is identical to JavaScript's โ // for a single line, /* */ for a block.
Printing Multiple Things
Println auto-spaces and joins multiple arguments, then adds a newline. Printf uses placeholder verbs (%d for a number, %s for a string, %v for "any value") inside a format string โ closer in spirit to a JavaScript template literal, but with explicit type placeholders instead of ${ }.
| Concept | JavaScript | Go |
|---|---|---|
| Run code | Browser / Node, instantly | go run file.go (compiles first) |
| Print output | console.log(...) | fmt.Println(...) |
| Entry point | Top-level script code | func main() { } |
| Unused variables | Allowed, silently ignored | Compile error |
Coding Challenges
Write a complete Go program (package main, import "fmt", func main) that prints your name and a short greeting on two separate lines using two calls to fmt.Println.
๐ View solutionWrite a program that uses fmt.Printf with %d to print "Year: 2026" and %s to print "Language: Go" โ two separate Printf calls, each using a placeholder rather than concatenation.
๐ View solutionWrite a program that declares a variable message with the value "Compiled and ready", but deliberately never uses it (no Println call referencing it). Try go run on it, note the exact compiler error, then fix it by actually printing the variable.
๐ View solutionChapter 1 Quick Reference
- package main โ marks a file as a runnable program
- import "fmt" โ brings in the formatting/printing package
- func main() { } โ the entry point; execution always starts here
- go run file.go โ compile and run immediately, for quick testing
- go build file.go โ produce a standalone executable to ship
- fmt.Println(...) โ print a line, auto-spaced and newline-terminated
- fmt.Printf("...%d...\n", x) โ formatted printing with explicit placeholders
- Unused variables/imports are compile errors, not warnings โ Go enforces this strictly
- Next chapter: variables, Go's basic types, and := vs var
Variables, Basic Types, and := vs var
Chapter 1's message := "Compiled and ready" already used a variable without explaining it. JavaScript's let/const (any value, any type, can hold anything later) has no direct equivalent here โ Go variables are statically typed: once a variable is created as a string, it can never later hold a number.
Declaring with var and an Explicit Type
var name string = "Philip" reads as: declare a variable called name, of type string, set to "Philip". The type is written explicitly between the name and the value โ the opposite order to let, where no type appears at all.
The Short Declaration Operator :=
:= declares a new variable AND infers its type from the value on the right, in one step โ far closer to JavaScript's const x = ... in everyday feel. It's the form used almost everywhere inside functions; var with an explicit type is mostly reserved for cases where no value is assigned yet, or the type genuinely needs to be different from what would be inferred.
name := "Philip" followed later by another name := "Sam" in the same scope is a compile error โ := declares, it doesn't just assign. To change an existing variable's value, drop the colon: name = "Sam".
Go's Core Basic Types
These four cover almost all everyday Go code: int, float64, string, bool. Unlike JavaScript's single general-purpose number type, Go separates whole numbers (int) from decimals (float64) โ mixing them directly causes a compile error, covered next chapter.
Zero Values โ Go Never Leaves a Variable Truly Empty
Declaring a variable with var and no value doesn't leave it as undefined the way JavaScript would โ Go automatically gives it a sensible default for its type, called the zero value. There is no Go equivalent of undefined; every variable always holds a real, usable value of its type from the moment it's declared.
Declaring Several Variables Together
Multiple variables can be declared on one line with :=, matching values to names left to right. The var ( ... ) block groups several explicit declarations together โ useful for constants and configuration-style values declared at the top of a file, outside any function.
| JavaScript | Go | Notes |
|---|---|---|
| let x = 5; | x := 5 | Type inferred automatically in both |
| let x; // undefined | var x int // 0 | Go has no "undefined" โ zero values fill the gap |
| x can later hold a string | x cannot โ fixed type forever | Static typing is the core difference |
| const for "shouldn't reassign" | No direct equivalent for variables | Go's const is for fixed, compile-time-known values only |
Coding Challenges
Write a program that declares title (string), pages (int), and inStock (bool) using := for each, then prints all three using a single fmt.Println call.
๐ View solutionWrite a program that declares var total int with no value, prints it (confirming it's 0, not an error), then assigns it 100 using = (not :=) and prints it again.
๐ View solutionWrite a program that declares city, population := "London", 8900000 on one line, then deliberately tries city := "Paris" again further down in main(). Run it, note the compiler error, then fix it using = instead of := for the second assignment.
๐ View solutionChapter 2 Quick Reference
- var name type = value โ explicit declaration with an explicit type
- name := value โ short declaration; type is inferred, only for NEW variables
- name = value (no colon) โ reassigns an EXISTING variable
- Core types: int, float64, string, bool
- Zero values: int โ 0, float64 โ 0, string โ "", bool โ false (no "undefined" in Go)
- Static typing โ a variable's type is fixed forever once declared
- var ( ... ) block โ group several explicit declarations together
- Next chapter: operators, arithmetic, and Go's strict rules about mixing numeric types
Operators, Arithmetic Type Rules, and Control Flow
Chapter 2 introduced int and float64 as separate types. This chapter shows why that separation matters the moment arithmetic is involved, then covers the operators and branching structures used to make decisions โ Go's version of Fundamentals JavaScript Chapter 3, with one notable omission: there is no ternary operator.
Arithmetic Operators
+ - * / % work as expected, but / between two int values performs integer division โ the decimal part is discarded entirely, not rounded. 10 / 3 gives exactly 3, never 3.33, because both operands are whole numbers.
var price float64 = 10 / 3 still produces 3 stored as a float โ the division itself happened entirely in int first, because both 10 and 3 were untyped integer literals. To get a real decimal result, at least one operand must actually be a float64: 10.0 / 3 gives 3.3333.... Mixing an actual int variable with a float64 variable directly (intVar / floatVar) is a compile error โ there is no automatic conversion, unlike JavaScript's number type.
Converting Between Types Explicitly
float64(count) explicitly converts an int to a float64 for that one expression. This explicitness โ having to ask for a conversion rather than it happening automatically โ is deliberate: it forces a decision about precision instead of letting a type mismatch hide silently.
Comparison and Logical Operators
== != > < >= <= and && || behave identically to JavaScript. One important difference: Go has no === โ there's only one equality operator, ==, since Go's static typing already guarantees both sides are the same type, removing the type-coercion ambiguity that == has in JavaScript.
if / else โ No Parentheses, Braces Required
The condition itself has no surrounding parentheses โ if age >= 18, not if (age >= 18) โ but the curly braces are mandatory, even for a single statement. Omitting them is a compile error, unlike JavaScript where braces are optional for one-line bodies.
switch โ Cleaner Than a Chain of else if
Go's switch needs no break at the end of each case โ unlike JavaScript, it never "falls through" to the next case automatically. A single case can also list several values separated by commas (case 2, 3, 4, 5:), avoiding repeated cases entirely.
condition ? a : b has no Go equivalent at all โ Go's designers left it out deliberately, favouring a full if/else even for simple cases, on the principle that it's more readable even though it's more verbose.
| JavaScript | Go | Notes |
|---|---|---|
| if (x) { } | if x { } | No parentheses around condition; braces always required |
| x === y | x == y | Only one equality operator โ types are already guaranteed to match |
| switch with break | switch, no break needed | Go never falls through by default |
| cond ? a : b | No equivalent | Must use a full if/else |
| 5 / 2 === 2.5 | 5 / 2 == 2 (int) | Integer division truncates; convert to float64 for a decimal result |
Coding Challenges
Write a program with two int variables, total := 17 and count := 5. Print total / count (integer division), then print the same calculation converted properly to a float64 result using float64(), so it shows the real decimal value.
๐ View solutionWrite a program with score := 72. Use if/else if/else to print "Pass with distinction" (score >= 85), "Pass" (score >= 50), or "Fail" (anything else).
๐ View solutionWrite a program with grade := "B". Use a switch statement to print a description for "A" ("Excellent"), "B" or "C" together ("Satisfactory"), and a default case ("Needs improvement") for anything else.
๐ View solutionChapter 3 Quick Reference
- + - * / % โ standard arithmetic; int / int truncates instead of rounding
- float64(x) โ explicit conversion; Go never converts numeric types automatically
- == != > < >= <= && || โ same meaning as JavaScript; only one equality operator (==)
- if x { } else if y { } else { } โ no parentheses around the condition, braces always mandatory
- switch x { case a: ... } โ no break needed, no automatic fall-through
- case a, b, c: โ multiple values in one case, comma-separated
- No ternary operator โ always use a full if/else instead
- Next chapter: loops โ for is Go's only loop keyword, covering every JavaScript loop style
Loops: for as Go's Only Loop Keyword
JavaScript has four loop keywords: for, while, for...of, for...in. Go has exactly one โ for โ and reshapes it to cover every one of those cases. Recognising which "shape" of for is in use is the main new skill this chapter teaches.
Shape 1: The Classic Counting Loop
This looks almost identical to JavaScript's for (let i = 0; i < 5; i++) โ minus the parentheses and semicolons-as-separators staying the same. i++ works exactly as before; Go also has i--, but notably no ++i prefix form โ increment/decrement are statements in Go, not expressions, so they're always written after the variable.
Shape 2: for as a while Loop
Dropping the init and post clauses, leaving only a condition, turns for into the exact equivalent of JavaScript's while โ there is no separate while keyword in Go at all, this is simply how it's written.
Shape 3: An Infinite Loop, with break
for with absolutely nothing after it loops forever, until something inside explicitly breaks out โ the Go equivalent of JavaScript's while (true) { ... break; }. continue also works exactly as it does in JavaScript, skipping straight to the next iteration.
Shape 4: for...range โ Iterating a Slice
[]string{...} is a slice โ Go's closest equivalent to a JavaScript array, covered properly next chapter. for index, value := range fruits is the direct equivalent of JavaScript's for...of, except it always hands back both the index AND the value together โ there's no separate index-only loop needed.
for _, fruit := range fruits { ... }. Go requires every declared variable to be used (Chapter 1), so _ exists specifically to say "I know this value exists, I'm deliberately not using it."
Looping Over a Map (Go's Object Equivalent)
The same range keyword also walks over a map (covered fully in Chapter 7) โ Go's rough equivalent of a JavaScript object โ handing back each key and value pair, the conceptual cousin of JavaScript's for...in.
range always visits elements 0, 1, 2... in order, ranging over a map visits entries in a deliberately randomised order every time the program runs. Code that depends on a specific order from a map will behave unpredictably โ sort the keys first if order matters.
| JavaScript | Go |
|---|---|
| for (let i = 0; i < n; i++) | for i := 0; i < n; i++ |
| while (cond) | for cond |
| while (true) { ... break; } | for { ... break } |
| for (const item of array) | for _, item := range slice |
| for (const key in object) | for key, value := range map |
Coding Challenges
Write a classic counting for loop that prints every even number from 0 to 10 (inclusive), using i += 2 in the post clause instead of i++.
๐ View solutionWrite a for loop used as a while loop: starting with balance := 100, keep subtracting 30 and printing the new balance each time, stopping (using the condition, not break) once balance would go below 0.
๐ View solutionGiven colours := []string{"red", "green", "blue", "yellow"}, use for...range with the blank identifier _ to print just the values, one per line, with no index shown.
๐ View solutionChapter 4 Quick Reference
- for i := 0; i < n; i++ { } โ classic counting loop, like JavaScript's for
- for condition { } โ Go's while loop; there is no separate while keyword
- for { } โ infinite loop; use break to exit, continue to skip an iteration
- for index, value := range slice { } โ like for...of, but always gives both index and value
- for key, value := range map { } โ like for...in, conceptually; order is NOT guaranteed
- _ (blank identifier) โ discards a value Go would otherwise require you to use
- No ++i prefix form โ i++ and i-- are statements, always written after the variable
- Next chapter: functions โ multiple return values, named returns, and variadic parameters
Functions: Multiple Returns, Named Returns, Variadic Params
Every function so far (main) has returned nothing. JavaScript functions return exactly one value โ wrapping several values up means returning an array or object instead. Go does something JavaScript can't: a function can return multiple, separate values directly, no wrapping required. This single feature underpins almost every standard-library function in Go, including fmt.Println's lesser-known cousin fmt.Sscanf and error handling throughout the language.
A Basic Function with Types on Both Ends
Each parameter's type follows its name (a int), and the return type follows the closing parenthesis () int {) โ there's no function keyword equivalent confusion here since Go only has one function syntax, unlike JavaScript's three (Fundamentals Chapter 5).
func add(a, b int) int means exactly the same as func add(a int, b int) int.
Multiple Return Values
(float64, string) in the return position declares two separate return values โ every return statement inside must supply both. The caller receives both at once with result, errMsg := divide(10, 2), destructuring-style, but without any array or object ever being created.
The error Type โ Go's Real Error-Handling Pattern
This is the real, idiomatic Go pattern, used constantly throughout the language: a function returns its normal result plus an error, which is nil (Go's null) when nothing went wrong. if err != nil immediately after almost every call is so common it's practically a Go signature โ this replaces the try/catch pattern from JavaScript Fundamentals Chapter 10 entirely; Go has no exceptions to throw or catch for ordinary error handling.
Named Return Values
Naming the return values in the function signature (area, perimeter float64) pre-declares them as variables, usable directly inside the function body. A bare return with nothing after it โ a naked return โ automatically sends back whatever those named variables currently hold. This is mostly a readability tool for short functions; longer functions are usually clearer with an explicit return area, perimeter.
Variadic Parameters โ Go's Version of Rest Parameters
...int before the parameter type marks it as variadic โ Go's direct equivalent of JavaScript's rest parameter (...numbers) from Intermediate Chapter 1. Inside the function, numbers behaves as a real slice, so range works on it exactly as it did in Chapter 4.
| JavaScript | Go |
|---|---|
| Returns one value (or an array/object of several) | func f() (int, string) โ returns several, separately |
| try/catch + throw | return value, error + if err != nil |
| function f(...args) | func f(args ...int) |
| No named return concept | func f() (result int) { ...; return } |
Coding Challenges
Write a function divide(a, b float64) (float64, error) that returns an error from the errors package if b is 0, otherwise the division result and nil. Call it twice โ once with b = 0, once with a real divisor โ handling both with if err != nil.
๐ View solutionWrite a function minMax(numbers ...int) (min, max int) using named return values and a naked return, that finds the smallest and largest values among any number of arguments. Call it with at least 5 numbers.
๐ View solutionWrite a function describeNumber(n int) (string, bool) that returns "even" or "odd" as the first value, and whether n is positive as the second (boolean) value. Call it with three different numbers, printing both returned values each time.
๐ View solutionChapter 5 Quick Reference
- func name(param type) returnType { } โ basic function shape
- func name() (typeA, typeB) { return a, b } โ multiple return values, no wrapping needed
- error type + nil โ Go's standard error pattern; check with if err != nil
- errors.New("message") โ creates a basic error value
- Named returns: func f() (result type) โ pre-declares the variable; "return" alone sends it back
- Variadic parameters: func f(args ...type) โ Go's equivalent of JavaScript's rest parameter
- No try/catch โ error handling is just normal values and normal if statements
- Next chapter: arrays and slices โ Go's two array-like types, and why slices are used almost everywhere
Arrays and Slices
Chapter 4 already used []string{...} without fully explaining it. Go has both arrays (fixed-size, rarely used directly) and slices (flexible, growable โ the real workhorse, and the closest equivalent to a JavaScript array). This chapter covers both, but spends most of its time on slices, since that's what real Go code uses almost everywhere.
Arrays โ Fixed Size, Part of the Type Itself
[3]int means "an array of exactly 3 ints" โ the size is part of the type itself, fixed forever once declared. [3]int and [4]int are considered entirely different types; an array can never grow or shrink. This rigidity is exactly why slices exist and are used almost everywhere instead.
Slices โ The Type You'll Actually Use
[]string โ square brackets with no number inside โ declares a slice, not an array. That missing number is the entire visual difference between the two, and it matters enormously: a slice can grow after creation, which an array never can.
Growing a Slice with append
append is the direct equivalent of JavaScript's push (Fundamentals Chapter 6) โ with one crucial difference: append returns a new slice rather than modifying in place, so the result must always be reassigned (numbers = append(numbers, 4)). Forgetting the reassignment is a common mistake โ the original variable simply won't reflect the new element.
append sometimes allocates a brand-new underlying array (when capacity runs out) and sometimes doesn't โ either way, always use the value append returns.
Slicing a Slice โ The [start:end] Syntax
letters[1:3] extracts a sub-slice, similar in spirit to JavaScript's array .slice() method, but built directly into Go's bracket syntax rather than a method call. The end index is always exclusive, the same convention as JavaScript's slice().
Iterating and Combining with Earlier Chapters
Combining Chapter 4's range with a running total is Go's version of JavaScript's reduce โ there's no built-in reduce method on slices in Go, so this manual loop pattern is the idiomatic replacement. %.2f in Printf formats a float to exactly 2 decimal places.
| JavaScript | Go |
|---|---|
| const arr = [1, 2, 3]; | arr := []int{1, 2, 3} (slice) |
| arr.push(4) | arr = append(arr, 4) |
| arr.length | len(arr) |
| arr.slice(1, 3) | arr[1:3] |
| arr.reduce((sum, x) => sum + x, 0) | manual for...range loop with a running total |
Coding Challenges
Create a slice named temps containing the floats 18.5, 22.0, 15.5, then use append to add 30.0 and 27.5 (in one call). Print the final slice and its length using len().
๐ View solutionCreate a slice named words containing 6 short strings of your choice. Print the first 3 using slicing syntax, then print the last 3 using slicing syntax, without using any literal index numbers higher than what's needed for each.
๐ View solutionCreate a slice of ints called scores with at least 5 values. Write a loop that calculates both the total and the average (total / number of scores, as a proper float64 result), printing both using Printf with %.2f.
๐ View solutionChapter 6 Quick Reference
- [N]Type โ a fixed-size array; size is part of the type, never grows
- []Type โ a slice; flexible, growable, the type used almost everywhere in real Go code
- append(slice, value) โ adds an element; ALWAYS reassign the result back
- len(slice) โ number of elements, works on arrays, slices, strings, and maps
- slice[start:end] โ sub-slice; end index is exclusive, either side can be omitted
- No built-in map/filter/reduce โ manual for...range loops are the idiomatic replacement
- %.2f โ Printf verb for a float formatted to 2 decimal places
- Next chapter: maps โ Go's key/value type, the rough equivalent of a JavaScript object
Maps
Chapter 4 briefly used map[string]int{...} while explaining range. A Go map is the closest equivalent to a JavaScript object's key/value behaviour (Fundamentals Chapter 7) โ except every key must be the same type, and every value must be the same type, declared up front as part of the map's own type.
Creating and Reading a Map
map[string]int reads as "a map from string keys to int values." Unlike JavaScript, where an object can hold a string here and a number there, every value in this map must be an int โ trying to add a string value would be a compile error.
Adding and Updating Entries
Adding and updating use the same bracket-assignment syntax โ Go decides which happened based on whether the key already existed, the same way JavaScript object property assignment works.
The Comma-Ok Idiom โ Checking If a Key Exists
Reading a missing key from a map doesn't error โ it silently returns the value type's zero value (Chapter 2), which can hide a real bug. value, exists := scores["french"] โ reading TWO things from a single map lookup โ is how Go distinguishes "the key exists and its value happens to be 0" from "the key genuinely doesn't exist." This pattern is called comma-ok, and appears constantly in real Go code.
scores["french"] alone (without comma-ok) returns 0 whether or not "french" was ever added โ there's no way to tell the difference from that single value alone. Always use the two-value form when "did this exist at all?" actually matters.
Deleting a Key
delete(map, key) is a built-in function (not a method) โ the direct equivalent of JavaScript's delete person.isStudent from Fundamentals Chapter 7.
Looking Ahead: structs (Briefly)
Maps are excellent for "any number of similarly-shaped entries" (a dictionary, a lookup table), but real-world records with a fixed set of named fields โ a person, a product, an order โ usually use a struct instead, Go's actual replacement for a JavaScript object literal. Structs get their own full treatment in Intermediate; this preview just establishes that p.Name uses the same dot notation as Chapter 7's JavaScript objects, even though the underlying type is completely different from a map.
| JavaScript | Go |
|---|---|
| { key: value } | map[string]int{"key": value} |
| obj.key = value | m[key] = value |
| delete obj.key | delete(m, key) |
| obj.key === undefined to check existence | value, exists := m[key] (comma-ok) |
| Mixed value types allowed | All values must be the same declared type |
| Fixed-shape record | struct (preview here, full coverage in Intermediate) |
Coding Challenges
Create a map stock := map[string]int{"apples": 10, "bananas": 5}. Add "oranges": 8 to it, update "apples" to 15, then print the whole map.
๐ View solutionUsing the same stock map, use the comma-ok idiom to check for "grapes" (which doesn't exist) and "apples" (which does), printing an appropriate message for each case.
๐ View solutionCreate a map inventory of at least 4 items with int quantities. Use a for...range loop to print each item with its quantity, and keep a running total using a plain variable, printing the total at the end. Then delete one item and print the map again to confirm it's gone.
๐ View solutionChapter 7 Quick Reference
- map[KeyType]ValueType{...} โ every key shares one type, every value shares another
- m[key] = value โ adds a new key or updates an existing one
- value, exists := m[key] โ comma-ok idiom; the only reliable way to check if a key exists
- delete(m, key) โ a built-in function, not a method
- for key, value := range m { } โ order is NOT guaranteed (Chapter 4)
- type Name struct { Field type } โ Go's fixed-shape record type, previewed here
- Maps: good for dynamic/lookup-style data. Structs: good for fixed-shape records
- This completes Go Fundamentals. Intermediate begins with structs in full, methods, and pointers.