Go in 2 Weeks #01 | Days 1–2: Go Philosophy & Core Syntax — A C++ Developer’s First Steps
이 글의 핵심
Your first steps in Go from a C++ background: installation, := and var, for and range, the GC, go fmt, and modules—always compared with C++ so the mental model clicks.
Series overview
📚 Go in 2 Weeks #01 | Full series index
This article covers Days 1–2 of the two-week Go curriculum for C++ developers.
Previous: Curriculum intro ← | → Next: #02 Memory & data structures
Introduction: meeting Go
Coming from C++, Go can feel almost too minimal: no headers, no template metaprogramming, and no manual new/delete. This article explains Go’s philosophy and core syntax from a C++ perspective. Build tooling elsewhere: CMake, Cargo / Rust, npm modules.
You will learn:
- Installing Go and the core toolchain (
go build,go run,go fmt) - Go’s values (simplicity, explicitness, concurrency) vs C++ (pointers, inheritance, generics)—high level
- Packages and modules (
go mod,import,internal) - Variables: how
:=differs from C++auto - Loops: a world with only
for - Garbage collection
go fmtas the style debate ender
Real-world notes
Moving from C++ to Go
After many years of C++ servers, Go felt surprisingly small—until production showed that small is a feature: faster iteration, fewer memory foot-guns, trivial deploys as a single binary.
Go’s philosophy: simplicity, explicitness, concurrency
Go optimizes for readable code and predictable behavior. Rather than many ways to do one thing, it prefers one clear idiom.
| Pillar | Meaning | In practice |
|---|---|---|
| Simplicity | Fewer keywords; tools like go fmt and go test enforce consistency | Less time arguing style or bespoke build scripts in review |
| Explicitness | Few implicit conversions; errors are values (if err != nil); concurrency starts with an explicit go | Failure modes show up in signatures |
| Concurrency | Goroutines + channels as the default story (covered later in the series) | Encourages synchronizing by communication |
To a C++ developer this can feel “less expressive,” but it usually lowers team-wide maintenance cost.
C++ vs Go: pointers, inheritance, generics
Pointers
- C++:
T*,T&, smart pointers, pointer arithmetic—coexist in one language. - Go: pointers are
*T, no pointer arithmetic. Nil dereference panics at runtime. Most “ownership” is handled by the GC. - Practice: passing addresses to avoid copies is familiar; dangling pointers into freed memory are largely prevented by design.
Inheritance
- C++:
publicinheritance, multiple inheritance,virtualfor polymorphism. - Go: no class inheritance. Use struct embedding and interfaces (method sets) instead (#03, #04).
- Practice: prefer small interfaces + composition over fat base classes.
Generics
- C++: templates for compile-time polymorphism—powerful, sometimes painful errors and build times.
- Go: type parameters since Go 1.18. Not everything must be generic; slices, maps, and channels already feel generic in daily use.
- Practice: learn interfaces and concrete types first; add generics for repeated algorithms or containers.
Packages and modules
Packages in one minute
- Every
.gofile belongs to apackage; executables usepackage mainandfunc main(). - Files in the same directory share the same package name.
- Exported names start with a capital letter; unexported names are package-private—think
namespace+ visibility.
Modules (go mod)
- A
go.modat the module root fixes the module path (e.g.github.com/org/project) as the import prefix. go mod init,go mod tidy; versions are pinned ingo.mod/go.sum.- Standard library imports use short paths (
"fmt","net/http").
Typical flow
mkdir myapp && cd myapp
go mod init github.com/you/myapp
go build ./...
go test ./...
internal: packages under myproject/internal/foo cannot be imported from outside the parent tree—useful for narrowing your public API.
Quick reference: functions and control flow
Functions
package example
import "fmt"
// Multiple return values (errors usually last)
func divide(a, b int) (int, error) {
if b == 0 {
return 0, fmt.Errorf("division by zero")
}
return a / b, nil
}
// Named results (use sparingly with defer)
func stats() (sum int, n int) {
// ...
return // naked return — avoid overuse
}
Control
- Conditions use
if,for,switchonly (nowhile). - Prefer
if err := f(); err != nil { ... }to keep scope tight.
See variables, loops, and conditionals for examples.
Table of contents
- Install & Hello World
- Go philosophy
- C++ vs Go (pointers, inheritance, generics)
- Packages & modules
- Syntax quick reference
- Variables:
autovs:= - Loops: one
forfor everything - Conditionals
- Garbage collection
- go fmt
- Exercises
1. Install Go and Hello World
Installing Go
# Windows (PowerShell): download .msi from https://go.dev/dl/
go version
# macOS (Homebrew)
brew install go
go version
# Linux: official tarball example
# wget https://go.dev/dl/go1.21.0.linux-amd64.tar.gz
# sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.21.0.linux-amd64.tar.gz
# export PATH=$PATH:/usr/local/go/bin
Use go version to verify the toolchain; go env shows GOROOT, GOPATH, GOOS, GOARCH—important for cross-compilation (e.g. GOOS=linux GOARCH=amd64 go build).
Hello World: C++ vs Go
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
Differences: package main is required; import not #include; func main() has no return type; go build / go run handle deps; static binary by default.
Command-line arguments
#include <iostream>
int main(int argc, char* argv[]) {
std::cout << "argc: " << argc << '\n';
for (int i = 0; i < argc; ++i) {
std::cout << "argv[" << i << "]: " << argv[i] << '\n';
}
return 0;
}
package main
import (
"fmt"
"os"
)
func main() {
args := os.Args
fmt.Println("len(args):", len(args))
for i, arg := range args {
fmt.Printf("args[%d]: %s\n", i, arg)
}
}
Core go commands
| Task | C++-ish | Go |
|---|---|---|
| Build | g++ ... | go build |
| Run | ./a.out | go run main.go |
| Format | clang-format | go fmt |
| Test | external frameworks | go test |
| Deps | Conan, vcpkg… | go mod |
2. Variables: auto vs :=
Go favors a small set of rules; C++ offers many styles (auto, constructors, constexpr, …).
Go declarations
var x int = 10
var y = 20
z := 30
const Max = 100
var uninitialized int
fmt.Println(uninitialized) // 0 — zero value, not garbage
a, b, c := 1, 2, 3
var (
name string = "Go"
version int = 1
)
Zero values
Uninitialized variables get zero values—no undefined behavior like reading uninitialized int in C++.
Short declaration :=
- Only inside functions
- At least one name must be new on the left-hand side when mixing with assignment
- Not allowed at package scope (use
var)
var vs :=
| Situation | Prefer |
|---|---|
| Inside function, obvious type | := |
| Package scope | var |
| Emphasize zero initialization | var |
| Need explicit type | var |
3. Loops: one for for everything
No while or do-while—only for.
Classic for
for i := 0; i < 10; i++ {
fmt.Println(i)
}
While-style
i := 0
for i < 10 {
fmt.Println(i)
i++
}
Infinite
for {
if shouldStop() {
break
}
}
range
nums := []int{1, 2, 3}
for i, v := range nums {
_, _ = i, v
}
for _, v := range nums { _ = v }
for i := range nums { _ = i }
break / continue / labels
outer:
for i := 0; i < 5; i++ {
for j := 0; j < 5; j++ {
if i*j > 10 {
break outer
}
}
}
4. Conditionals
if (no parentheses around the condition)
x := 10
if x > 5 {
fmt.Println("x is greater than 5")
} else if x > 0 {
fmt.Println("x is positive")
} else {
fmt.Println("x is non-positive")
}
// Single-line bodies still need braces
if x > 5 {
fmt.Println("Greater")
}
// Short declaration limits scope
if v, ok := m[key]; ok {
fmt.Println("Found:", v)
}
if err := doSomething(); err != nil {
fmt.Println("Error:", err)
return
}
// import "os" (and "fmt") in real code
if file, err := os.Open("data.txt"); err != nil {
panic(err)
} else {
defer file.Close()
_ = file
}
| C++ | Go | |
|---|---|---|
| Condition parens | Required | Omitted |
| Braces | Optional one-liner | Always required |
| Init + condition | C++17 if-init | Supported from the start |
switch (no implicit fall-through)
day := 3
switch day {
case 1:
fmt.Println("Monday")
case 2:
fmt.Println("Tuesday")
case 3, 4:
fmt.Println("Mid-week")
default:
fmt.Println("Other day")
}
// Tagless switch (replaces if-else chains)
x := 15
switch {
case x < 0:
fmt.Println("Negative")
case x < 10:
fmt.Println("Small")
case x < 100:
fmt.Println("Medium")
default:
fmt.Println("Large")
}
Use fallthrough explicitly when you need C-style fall-through.
5. Garbage collection: beyond new/delete
C++: manual control (performance possible, bugs likely).
Go: GC-managed heap (safety and velocity).
C++ (manual)
int* p = new int(42);
delete p;
int* arr = new int[100];
delete[] arr;
Go (automatic)
p := new(int)
*p = 42
person := &Person{Name: "Alice", Age: 30}
arr := make([]int, 100)
m := make(map[string]int)
m["key"] = 100
Pros: fewer leaks, no dangling pointers from delete, less cognitive load.
Cons: GC pauses (usually small in Go), higher RSS than tuned C++, not ideal for the hardest real-time constraints.
Escape analysis
go build -gcflags="-m" main.go
# Example: "moved to heap", "escapes to heap"
| C++ | Go | |
|---|---|---|
| Free | delete / RAII | GC |
| Array delete | delete[] | N/A |
| Leaks | Common foot-gun | Rare |
| Dangling | Possible | Effectively prevented |
6. go fmt: end the style war
C++ style varies wildly (K&R, Allman, GNU, …). Go has one canonical layout—go fmt applies it with no config file.
go fmt main.go
go fmt ./...
gofmt -w main.go
gofmt -d main.go
VS Code (example):
{
"go.formatTool": "gofmt",
"editor.formatOnSave": true,
"[go]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
}
}
7. Exercises
Exercise 1: FizzBuzz
#include <iostream>
int main() {
for (int i = 1; i <= 100; i++) {
if (i % 15 == 0) {
std::cout << "FizzBuzz\n";
} else if (i % 3 == 0) {
std::cout << "Fizz\n";
} else if (i % 5 == 0) {
std::cout << "Buzz\n";
} else {
std::cout << i << "\n";
}
}
return 0;
}
package main
import "fmt"
func main() {
for i := 1; i <= 100; i++ {
switch {
case i%15 == 0:
fmt.Println("FizzBuzz")
case i%3 == 0:
fmt.Println("Fizz")
case i%5 == 0:
fmt.Println("Buzz")
default:
fmt.Println(i)
}
}
}
Exercise 2: multiplication table
package main
import "fmt"
func main() {
for i := 2; i <= 9; i++ {
for j := 1; j <= 9; j++ {
fmt.Printf("%d x %d = %d\n", i, j, i*j)
}
fmt.Println()
}
}
Exercise 3: slice sum
#include <iostream>
#include <vector>
int sum(const std::vector<int>& numbers) {
int total = 0;
for (int num : numbers) {
total += num;
}
return total;
}
package main
import "fmt"
func sum(numbers []int) int {
total := 0
for _, num := range numbers {
total += num
}
return total
}
Exercise 4: filter even numbers
std::vector<int> filterEven(const std::vector<int>& numbers) {
std::vector<int> result;
result.reserve(numbers.size());
for (int num : numbers) {
if (num % 2 == 0) {
result.push_back(num);
}
}
return result;
}
func filterEven(numbers []int) []int {
result := make([]int, 0, len(numbers))
for _, num := range numbers {
if num%2 == 0 {
result = append(result, num)
}
}
return result
}
Exercise 5: word frequency
package main
import "fmt"
func main() {
words := []string{"apple", "banana", "apple", "cherry", "banana", "apple"}
freq := make(map[string]int)
for _, word := range words {
freq[word]++
}
for word, count := range freq {
fmt.Printf("%s: %d\n", word, count)
}
}
8. Days 1–2 checklist
- Install Go;
go versionworks - Hello World with
go run/go build - Understand
:=vsvarand zero values - Use
forfor every loop style +range -
ifwith short declaration;switchwithout manualbreak - Run
go fmt ./... - Understand
go mod, import paths, exported names - Complete the exercises
9. What’s next
#02 covers pointers (no arithmetic), slices, maps, and structs—in depth.
📚 Series navigation
| Previous | Index | Next |
|---|---|---|
| ← Curriculum | 📑 Index | #02 Data structures → |
Go in 2 weeks: Curriculum • #01 • #02 • #03 • #04 • #05 • #06 • #07 • #08 • #09
TL;DR: Go trims C++ complexity; master :=, for, and go fmt and you’ve finished the first milestone.
Related reading
- Go series index
- Two-week Go curriculum
- C++ developer’s view of Go
- C++ vs Go
Keywords
Go tutorial, Golang install, short variable declaration, Go for loop, range, garbage collector, go fmt, Go vs C++, golang basics.
Practical tips
- Fix compiler warnings first; reproduce bugs with minimal examples.
- Profile before optimizing.
FAQ
Q. When do I use this in production?
A. Day-to-day: declaring variables, looping, formatting with go fmt, and trusting the GC—exactly the baseline for services and CLIs.
Q. Prerequisites?
A. Follow the curriculum from the intro or the C++ series index.
Q. Go deeper?
A. go.dev/doc and cppreference for C++ parallels.
Related posts
- Two-week Go curriculum
- C++ vs Go
- C++ developer’s view of Go
- [Go #02] Memory & data structures
- [Go #03] OOP without classes