Go Quiz & Coding Challenges
How to use: Go section by section. For "What's the output?" — reason through it before expanding. For coding challenges — write the solution first, then verify.
Section 1 — Variables & Types
Q1 — What's the output?
package main
import "fmt"
var x = 10
func main() {
x := 20
fmt.Println(x)
{
x := 30
fmt.Println(x)
}
fmt.Println(x)
}
[!success]- Answer
20 30 20
x := 20shadows the package-levelxinsidemain. The inner block creates another shadow. The outermainxis unaffected throughout.
Q2 — Will this compile? If not, why?
package main
const MaxItems = 100
func main() {
MaxItems = 200
x, y := 1, 2
fmt.Println(x)
}
[!success]- Answer No — two compile errors:
MaxItems = 200— constants cannot be reassignedydeclared but not used — Go requires all declared variables to be used
Q3 — What does this print?
func main() {
var a int
var b float64
var c string
var d bool
fmt.Printf("%v %v %q %v\n", a, b, c, d)
}
[!success]- Answer
0 0 "" falseZero values:
int→0,float64→0,string→""(printed quoted with%q),bool→false.
Q4 — Fix the bug
func main() {
x := 5
y := 2.5
fmt.Println(x + y)
}
[!success]- Answer
fmt.Println(float64(x) + y) // or fmt.Println(x + int(y))Go has no implicit type conversion.
int + float64is a compile error — you must convert explicitly.
Q5 — Coding Challenge
Write a function celsiusToFahrenheit(c float64) float64 and use iota to define constants for three temperature scales: Celsius = 0, Fahrenheit = 1, Kelvin = 2.
[!success]- Answer
type TempScale int const ( Celsius TempScale = iota // 0 Fahrenheit // 1 Kelvin // 2 ) func celsiusToFahrenheit(c float64) float64 { return c*9/5 + 32 }
Section 2 — Control Flow (if / switch)
Q6 — What's the output?
func classify(n int) string {
switch {
case n < 0:
return "negative"
case n == 0:
return "zero"
case n < 10:
return "small"
default:
return "large"
}
}
func main() {
fmt.Println(classify(-3))
fmt.Println(classify(0))
fmt.Println(classify(7))
fmt.Println(classify(100))
}
[!success]- Answer
negative zero small large
switchwith no condition evaluates each case as a boolean — equivalent toswitch true.
Q7 — What's the output?
func main() {
x := 2
switch x {
case 1:
fmt.Println("one")
fallthrough
case 2:
fmt.Println("two")
fallthrough
case 3:
fmt.Println("three")
case 4:
fmt.Println("four")
}
}
[!success]- Answer
two threeExecution starts at
case 2(matched).fallthroughunconditionally continues tocase 3. Nofallthroughincase 3, so it stops.case 4is never reached.
Q8 — Fix the bug
func main() {
if score := 85
score >= 90 {
fmt.Println("A")
} else {
fmt.Println("B")
}
}
[!success]- Answer
if score := 85; score >= 90 { fmt.Println("A") } else { fmt.Println("B") }The init statement and condition must be separated by a semicolon
;, not a newline.
Q9 — Coding Challenge
Write dayType(day string) string that returns "weekend" for Saturday/Sunday and "weekday" for everything else. Use a switch with multiple values per case.
[!success]- Answer
func dayType(day string) string { switch day { case "Saturday", "Sunday": return "weekend" default: return "weekday" } }
Section 3 — Functions
Q10 — What's the output?
func mystery() (result int) {
defer func() {
result++
}()
return 10
}
func main() {
fmt.Println(mystery())
}
[!success]- Answer
11
return 10sets the named returnresult = 10. Then the deferred function runs and incrementsresultto 11. Named returns let defers modify the return value afterreturn.
Q11 — What's the output?
func main() {
for i := 0; i < 3; i++ {
defer fmt.Println(i)
}
}
[!success]- Answer
2 1 0Defer is LIFO — last registered runs first. Arguments are evaluated immediately at the
deferstatement, so each defer captures its current value ofi(0, 1, 2). They run in reverse: 2, 1, 0.
Q12 — Is there a bug? What does it print?
func makeMultiplier(factor int) func(int) int {
return func(n int) int {
return n * factor
}
}
func main() {
double := makeMultiplier(2)
triple := makeMultiplier(3)
fmt.Println(double(5))
fmt.Println(triple(5))
fmt.Println(double(triple(4)))
}
[!success]- Answer No bug. Output:
10 15 24
double(triple(4))=double(12)=24. Each call tomakeMultipliercreates an independent closure that captures its ownfactor.
Q13 — Coding Challenge
Write a variadic function stats(nums ...float64) (min, max, avg float64) that returns the minimum, maximum, and average of any number of floats.
[!success]- Answer
func stats(nums ...float64) (min, max, avg float64) { if len(nums) == 0 { return } min, max = nums[0], nums[0] sum := 0.0 for _, n := range nums { if n < min { min = n } if n > max { max = n } sum += n } avg = sum / float64(len(nums)) return } mn, mx, av := stats(3, 1, 4, 1, 5, 9, 2, 6) fmt.Printf("min=%.0f max=%.0f avg=%.2f\n", mn, mx, av) // min=1 max=9 avg=3.88
Section 4 — Structs
Q14 — What's the output?
type Point struct{ X, Y int }
func move(p Point, dx, dy int) {
p.X += dx
p.Y += dy
}
func movePtr(p *Point, dx, dy int) {
p.X += dx
p.Y += dy
}
func main() {
p := Point{1, 2}
move(p, 5, 5)
fmt.Println(p)
movePtr(&p, 5, 5)
fmt.Println(p)
}
[!success]- Answer
{1 2} {6 7}
movereceives a copy — original unchanged.movePtrreceives the address — modifies the original.
Q15 — What's the output?
type Animal struct{ Sound string }
type Dog struct {
Animal
Name string
}
func (a Animal) Speak() string { return a.Sound }
func (d Dog) Speak() string { return d.Name + " says " + d.Sound }
func main() {
d := Dog{Animal: Animal{Sound: "woof"}, Name: "Rex"}
fmt.Println(d.Sound)
fmt.Println(d.Animal.Speak())
fmt.Println(d.Speak())
}
[!success]- Answer
woof woof Rex says woof
d.Sound— promoted field from embeddedAnimal.d.Animal.Speak()— calls Animal's method directly.d.Speak()— Dog's own method takes priority over the promoted one.
Q16 — Coding Challenge
Define a Rectangle struct with Width and Height float64. Add methods: Area(), Perimeter(), IsSquare() bool, and Scale(factor float64) that mutates the receiver.
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter float64 {
return 2 * (r.Width + r.Height)
}
func (r Rectangle) IsSquare bool {
return r.Width == r.Height
}
func (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}
[!success]- Answer
type Rectangle struct { Width, Height float64 } func (r Rectangle) Area() float64 { return r.Width * r.Height } func (r Rectangle) Perimeter() float64 { return 2 * (r.Width + r.Height) } func (r Rectangle) IsSquare() bool { return r.Width == r.Height } func (r *Rectangle) Scale(factor float64) { r.Width *= factor r.Height *= factor } r := Rectangle{Width: 4, Height: 6} fmt.Println(r.Area()) // 24 fmt.Println(r.IsSquare()) // false r.Scale(2) fmt.Println(r.Width) // 8
Section 5 — Interfaces
Q17 — Will this compile? Why?
type Speaker interface{ Speak() string }
type Cat struct{ Name string }
func (c Cat) Speak() string { return c.Name + " says meow" }
type Robot struct{ ID int }
func (r *Robot) Speak() string { return fmt.Sprintf("Robot %d beeping", r.ID) }
func makeNoise(s Speaker) { fmt.Println(s.Speak()) }
func main() {
makeNoise(Cat{Name: "Luna"})
makeNoise(Robot{ID: 1}) // line A
makeNoise(&Robot{ID: 2}) // line B
}
[!success]- Answer Line A does NOT compile.
Robot(value type) does not satisfySpeakerbecauseSpeakhas a pointer receiver — only*Robotsatisfies the interface. Line B compiles fine.Fix:
makeNoise(&Robot{ID: 1})
Q18 — What's the output?
func typeCheck(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("int: %d\n", v*2)
case string:
fmt.Printf("string: %q (len %d)\n", v, len(v))
case bool:
fmt.Printf("bool: %v\n", !v)
default:
fmt.Printf("other: %T\n", v)
}
}
func main() {
typeCheck(21)
typeCheck("hello")
typeCheck(true)
typeCheck(3.14)
}
[!success]- Answer
int: 42 string: "hello" (len 5) bool: false other: float64
Q19 — Coding Challenge
Define a Shape interface with Area() float64. Implement it for Circle (field Radius) and Triangle (fields Base, Height). Write totalArea(shapes []Shape) float64.
import "math"
type Shape interface { Area() float64 }
type Circle struct {
Radius float64
}
type Traingle struct {
Base float64
Height float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func (t Traingle) Area() float64 {
return 0.5 * t.Base * t.Height
}
func totalArea(shapes []Shape) float64 {
ans := 0.0
for _, s := range shapes {
ans += s.Area()
}
return ans
}
[!success]- Answer
import "math" type Shape interface{ Area() float64 } type Circle struct{ Radius float64 } type Triangle struct{ Base, Height float64 } func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius } func (t Triangle) Area() float64 { return 0.5 * t.Base * t.Height } func totalArea(shapes []Shape) float64 { total := 0.0 for _, s := range shapes { total += s.Area() } return total } shapes := []Shape{ Circle{Radius: 3}, Triangle{Base: 4, Height: 6}, Circle{Radius: 1}, } fmt.Printf("%.2f\n", totalArea(shapes)) // 31.56
Section 6 — Errors
Q20 — What's the output?
import "errors"
var ErrDivByZero = errors.New("division by zero")
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("divide(%.0f, %.0f): %w", a, b, ErrDivByZero)
}
return a / b, nil
}
func main() {
_, err := divide(10, 0)
fmt.Println(err)
fmt.Println(errors.Is(err, ErrDivByZero))
}
[!success]- Answer
divide(10, 0): division by zero true
%wwrapsErrDivByZeroinside the formatted error.errors.Istraverses the chain and finds it.
Q21 — What's the output?
func riskyOp() {
defer func() {
if r := recover(); r != nil {
fmt.Println("recovered:", r)
}
}()
panic("boom")
fmt.Println("after panic")
}
func main() {
riskyOp()
fmt.Println("main continues")
}
[!success]- Answer
recovered: boom main continues
recover()inside a deferred function catches the panic."after panic"is never reached. Execution resumes normally inmainafterriskyOpreturns.
Q22 — Coding Challenge
Create a custom error type ValidationError with fields Field and Message string. Write validateUsername(name string) error — returns an error if name is empty or shorter than 3 chars. The caller should use errors.As to extract the field name.
[!success]- Answer
type ValidationError struct { Field string Message string } func (e *ValidationError) Error() string { return fmt.Sprintf("field %q: %s", e.Field, e.Message) } func validateUsername(name string) error { if name == "" { return &ValidationError{Field: "username", Message: "cannot be empty"} } if len(name) < 3 { return &ValidationError{Field: "username", Message: "must be at least 3 characters"} } return nil } err := validateUsername("ab") var ve *ValidationError if errors.As(err, &ve) { fmt.Printf("invalid field: %s — %s\n", ve.Field, ve.Message) } // invalid field: username — must be at least 3 characters
Section 7 — Loops
Q23 — What's the output?
func main() {
s := []int{1, 2, 3, 4, 5}
for _, v := range s {
v *= 10
}
fmt.Println(s)
}
[!success]- Answer
[1 2 3 4 5]
vis a copy of each element — modifying it doesn't affect the original slice. To modify in place uses[i] *= 10.
Q24 — What's the output?
func main() {
outer:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if j == 2 {
continue outer
}
fmt.Printf("%d%d ", i, j)
}
}
}
[!success]- Answer
00 01 10 11 20 21When
j == 2,continue outerskips the rest of the inner loop and jumps to the nextiiteration. Soj=2is never printed for anyi.
Q25 — Coding Challenge
Write fizzbuzz(n int) []string — returns a slice where multiples of 3 are "Fizz", multiples of 5 are "Buzz", multiples of both are "FizzBuzz", otherwise the number as a string.
[!success]- Answer
import "strconv" func fizzbuzz(n int) []string { result := make([]string, 0, n) for i := 1; i <= n; i++ { switch { case i%15 == 0: result = append(result, "FizzBuzz") case i%3 == 0: result = append(result, "Fizz") case i%5 == 0: result = append(result, "Buzz") default: result = append(result, strconv.Itoa(i)) } } return result }
Section 8 — Slices
Q26 — What's the output?
func main() {
a := []int{1, 2, 3, 4, 5}
b := a[1:4]
b[0] = 99
fmt.Println(a)
fmt.Println(b)
fmt.Println(len(b), cap(b))
}
[!success]- Answer
[1 99 3 4 5] [99 3 4] 3 4
bis a window intoa— they share the same backing array. Modifyingb[0]changesa[1].cap(b) = 4because there are 4 elements fromb's start pointer to the end ofa.
Q27 — What's the output?
func main() {
s := make([]int, 3, 5)
s = append(s, 1)
s = append(s, 2)
fmt.Println(len(s), cap(s))
s = append(s, 3)
fmt.Println(len(s), cap(s))
}
[!success]- Answer
5 5 6 10
make([]int, 3, 5)→ len=3, cap=5. Two appends fill to len=5 within capacity (no realloc). The third append exceeds cap=5 so Go allocates a new array (typically 2× → cap=10).
Q28 — Coding Challenge
Write removeDuplicates(s []int) []int — returns a new slice with duplicates removed, preserving original order.
[!success]- Answer
func removeDuplicates(s []int) []int { seen := make(map[int]struct{}) result := make([]int, 0, len(s)) for _, v := range s { if _, exists := seen[v]; !exists { seen[v] = struct{}{} result = append(result, v) } } return result } fmt.Println(removeDuplicates([]int{1, 2, 2, 3, 1, 4, 3})) // [1 2 3 4]
Q29 — Coding Challenge
Write rotate(s []int, k int) []int that rotates the slice left by k positions in-place using the reverse trick.
[!success]- Answer
func reverse(s []int) { for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { s[i], s[j] = s[j], s[i] } } func rotate(s []int, k int) []int { n := len(s) if n == 0 { return s } k = k % n reverse(s[:k]) reverse(s[k:]) reverse(s) return s } s := []int{1, 2, 3, 4, 5} fmt.Println(rotate(s, 2)) // [3 4 5 1 2]
Section 9 — Maps
Q30 — What's the output?
func main() {
m := map[string]int{"a": 1, "b": 2}
v1 := m["a"]
v2 := m["z"]
v3, ok := m["z"]
fmt.Println(v1, v2, ok, v3)
}
[!success]- Answer
1 0 false 0
m["z"]returns0(zero value for int) — not an error. The comma-ok form distinguishes "key missing" (ok=false) from "key present with zero value".
Q31 — Will this panic? Why?
func main() {
var m map[string]int
fmt.Println(m["key"])
m["key"] = 1
}
[!success]- Answer Yes — panics on the write.
m["key"]read is safe — returns0.m["key"] = 1panics:assignment to entry in nil map.Fix:
m = make(map[string]int)before writing.
Q32 — Coding Challenge
Write wordCount(s string) map[string]int that returns how many times each word appears in the string.
[!success]- Answer
import "strings" func wordCount(s string) map[string]int { counts := make(map[string]int) for _, word := range strings.Fields(s) { counts[word]++ } return counts } fmt.Println(wordCount("go is great and go is fast")) // map[and:1 fast:1 go:2 great:1 is:2]
strings.Fieldssplits on any whitespace.counts[word]++works even when the key is absent — zero value0is incremented to1.
Q33 — Coding Challenge
Write groupByLength(words []string) map[int][]string that groups words by their character length.
[!success]- Answer
func groupByLength(words []string) map[int][]string { groups := make(map[int][]string) for _, w := range words { groups[len(w)] = append(groups[len(w)], w) } return groups } words := []string{"go", "is", "fun", "and", "fast", "cool"} fmt.Println(groupByLength(words)) // map[2:[go is] 3:[fun and] 4:[fast cool]]Appending to a nil slice value in a map is safe —
append(nil, w)creates a new slice.
Section 10 — Pointers
Q34 — What's the output?
func increment(n *int) {
*n++
}
func main() {
x := 5
increment(&x)
increment(&x)
fmt.Println(x)
}
[!success]- Answer
7
incrementreceives the address ofxand increments the actual value through the pointer. Called twice → 5 + 1 + 1 = 7.
Q35 — What's the output?
type Counter struct{ n int }
func (c Counter) Inc() { c.n++ }
func (c *Counter) PInc() { c.n++ }
func main() {
c := Counter{}
c.Inc()
c.Inc()
fmt.Println(c.n)
c.PInc()
c.PInc()
fmt.Println(c.n)
}
[!success]- Answer
0 2
Inchas a value receiver — operates on a copy, original unchanged.PInchas a pointer receiver — modifies the original. After twoInccalls,c.nis still0. After twoPInccalls,c.nis2.
Q36 — Is there a bug?
func newInt(n int) *int {
return &n
}
func main() {
p := newInt(42)
fmt.Println(*p)
}
[!success]- Answer No bug. Output:
42In Go, returning a pointer to a local variable is safe — the compiler detects that the value escapes the function and allocates it on the heap automatically. Unlike C, this is perfectly valid.
Q37 — Coding Challenge
Write swap(a, b *int) that swaps two integers using pointers. Then show the idiomatic Go way to swap without pointers.
[!success]- Answer
// Using pointers func swap(a, b *int) { *a, *b = *b, *a } x, y := 10, 20 swap(&x, &y) fmt.Println(x, y) // 20 10 // Idiomatic Go — no pointers needed x, y = y, x fmt.Println(x, y) // 10 20
Section 11 — Packages & Modules
Q38 — Concept Questions
a) What's the difference between a package and a module?
b) Why does import _ "image/png" not cause an "imported and not used" error?
c) Is process in package util exported? What about Process?
d) Can two packages import each other?
[!success]- Answer a) A package is a directory of
.gofiles sharing apackagedeclaration — the unit of code organisation. A module is a collection of packages defined by ago.modfile — the unit of versioning and distribution.b) The blank import
_explicitly discards the package name. It's a deliberate signal: "run this package'sinit()for side effects only" (e.g., registering a database driver or image decoder).c)
process— unexported (lowercase), only usable withinpackage util.Process— exported (uppercase), usable by any package that importsutil.d) No. Circular imports are a compile error in Go. Fix by extracting shared types into a third package that both can import.
Q39 — Will this compile?
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println("Pi is approximately 3.14")
}
[!success]- Answer No — compile error:
"math" imported and not used.Go requires every import to be used. Remove
"math"to fix it.
Mixed Challenges
Q40 — Put it all together
Write a complete program that:
- Defines a
Studentstruct withName stringandGrades []float64 - Adds
Average() (float64, error)— returnsErrNoGradesif slice is empty - Adds
Grade() stringreturning"A"/"B"/"C"/"F"based on average (90+ / 80+ / 70+ / below) - Defines a
Reporterinterface withReport() stringand implements it onStudent - Writes
printReports(reporters []Reporter)that prints each report
import "errors"
type Student struct {
Name string
Grades []float64
}
type Reporter interface {
Report() string
}
func (s Student) Average() (float64, error) {
if len(s.Grades) == 0 {
return 0.0, errors.New("ErrNoGrades")
}
total := 0.0
for _, grade := range s.Grades {
total += grade
}
return total / float64(len(s.Grades)), nil
}
func (s Student) Grade() string {
avg, err = s.Average()
if err != nil {
return "N/A"
}
switch {
case avg >= 90:
return "A"
case avg >= 80:
return "B"
case avg >= 70:
return "C"
default:
return "F"
}
}
func (s Student) Report() string {
avg, err := s.Average()
if err != nil {
return fmt.Sprintf("%s: no grades", s.Name)
}
return fmt.Sprintf("%s: avg=%.1f grade:%v", s.Name, avg, s.Grade())
}
func printReports(reporters []Reporter) {
for _, r := range reporters {
fmt.Println(r.Report())
}
}
[!success]- Answer
package main import ( "errors" "fmt" ) var ErrNoGrades = errors.New("no grades recorded") type Student struct { Name string Grades []float64 } func (s *Student) Average() (float64, error) { if len(s.Grades) == 0 { return 0, ErrNoGrades } sum := 0.0 for _, g := range s.Grades { sum += g } return sum / float64(len(s.Grades)), nil } func (s *Student) Grade() string { avg, err := s.Average() if err != nil { return "N/A" } switch { case avg >= 90: return "A" case avg >= 80: return "B" case avg >= 70: return "C" default: return "F" } } func (s *Student) Report() string { avg, err := s.Average() if err != nil { return fmt.Sprintf("%s: no grades", s.Name) } return fmt.Sprintf("%s: avg=%.1f grade=%s", s.Name, avg, s.Grade()) } type Reporter interface{ Report() string } func printReports(reporters []Reporter) { for _, r := range reporters { fmt.Println(r.Report()) } } func main() { students := []Reporter{ &Student{Name: "Alice", Grades: []float64{92, 88, 95}}, &Student{Name: "Bob", Grades: []float64{72, 68, 75}}, &Student{Name: "Carol", Grades: []float64{}}, } printReports(students) // Alice: avg=91.7 grade=A // Bob: avg=71.7 grade=C // Carol: no grades }
Q41 — Spot All the Bugs
func main() {
var m map[string][]int
m["scores"] = append(m["scores"], 95)
s := make([]int, 5)
s[5] = 10
var p *int
*p = 42
const limit = 100
limit = 200
}
[!success]- Answer Four bugs:
m["scores"] = ...— write to nil map → panic. Fix:m = make(map[string][]int)s[5] = 10— index out of bounds, valid indices are 0–4 → panic. Fix:make([]int, 6)orappend(s, 10)*p = 42— nil pointer dereference → panic. Fix:n := 42; p = &nlimit = 200— cannot assign to a constant → compile error. Fix: usevar limit = 100
Section 12 — Channels & Goroutines
Q42 — What's the output?
func main() {
ch := make(chan int)
go func() {
ch <- 1
ch <- 2
ch <- 3
close(ch)
}()
for v := range ch {
fmt.Println(v)
}
fmt.Println("done")
}
[!success]- Answer
1 2 3 done
rangeover a channel reads until the channel is closed. The goroutine sends 3 values then closes —rangeexits cleanly and execution continues.
Q43 — Will this deadlock? Why?
func main() {
ch := make(chan string)
ch <- "hello"
fmt.Println(<-ch)
}
[!success]- Answer Yes — deadlocks immediately.
chis unbuffered.ch <- "hello"blocks waiting for a receiver, butmainis the only goroutine and it's stuck on the send — there's nobody to receive. Go detects this and panics:all goroutines are asleep - deadlock!Fix: launch the send in a goroutine, or use
make(chan string, 1).
Q44 — What's the output?
func main() {
ch := make(chan int, 2)
ch <- 10
ch <- 20
select {
case v := <-ch:
fmt.Println("received:", v)
default:
fmt.Println("nothing ready")
}
select {
case v := <-ch:
fmt.Println("received:", v)
default:
fmt.Println("nothing ready")
}
select {
case v := <-ch:
fmt.Println("received:", v)
default:
fmt.Println("nothing ready")
}
}
[!success]- Answer
received: 10 received: 20 nothing readyThe buffered channel has 2 values. The first two selects receive them. The third select finds the channel empty —
defaultruns immediately instead of blocking.
Q45 — What's the output?
func main() {
var c chan int
select {
case v := <-c:
fmt.Println("received:", v)
default:
fmt.Println("nil channel, skipped")
}
}
[!success]- Answer
nil channel, skippedA receive from a nil channel blocks forever — but
selectwith adefaultcase never blocks. Since the nil channel case is never ready,defaultfires immediately.
Q46 — Coding Challenge
Write a pipeline function: one goroutine generates squares of 1–5 on a channel, a second goroutine reads them and doubles each value, the main goroutine prints the results. Use channel direction types on each stage.
[!success]- Answer
func generate(out chan<- int) { for i := 1; i <= 5; i++ { out <- i * i } close(out) } func double(in <-chan int, out chan<- int) { for v := range in { out <- v * 2 } close(out) } func main() { squares := make(chan int) doubled := make(chan int) go generate(squares) go double(squares, doubled) for v := range doubled { fmt.Println(v) // 2 8 18 32 50 } }
Section 13 — Mutexes
Q47 — What's the bug?
type SafeMap struct {
mu sync.Mutex
data map[string]int
}
func (m SafeMap) Set(key string, val int) {
m.mu.Lock()
defer m.mu.Unlock()
m.data[key] = val
}
[!success]- Answer Two bugs:
- Value receiver copies the mutex —
SafeMapis copied on every call toSet, which copiessync.Mutextoo. A copied mutex is always unlocked regardless of the original's state.go vetflags this.- Value receiver copies the map header — while maps are reference types, the copy of
SafeMaphas an independent header; ifdatawere ever replaced (e.g.m.data = make(...)) the caller wouldn't see it.Fix: use a pointer receiver —
func (m *SafeMap) Set(...).
Q48 — What's the output?
type RWCounter struct {
mu sync.RWMutex
value int
}
func (c *RWCounter) Inc() {
c.mu.Lock()
defer c.mu.Unlock()
c.value++
}
func (c *RWCounter) Get() int {
c.mu.RLock()
defer c.mu.RUnlock()
return c.value
}
func main() {
c := &RWCounter{}
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
c.Inc()
}()
}
wg.Wait()
fmt.Println(c.Get())
}
[!success]- Answer
55 goroutines each call
Inconce.sync.MutexinsideIncserialises the writes — no race condition.GetusesRLock(safe for concurrent reads). Result is always5.
Q49 — Coding Challenge
Write a Cache[K comparable, V any] generic struct with Set(key K, val V) and Get(key K) (V, bool) methods, safe for concurrent use.
[!success]- Answer
type Cache[K comparable, V any] struct { mu sync.RWMutex items map[K]V } func NewCache[K comparable, V any]() *Cache[K, V] { return &Cache[K, V]{items: make(map[K]V)} } func (c *Cache[K, V]) Set(key K, val V) { c.mu.Lock() defer c.mu.Unlock() c.items[key] = val } func (c *Cache[K, V]) Get(key K) (V, bool) { c.mu.RLock() defer c.mu.RUnlock() v, ok := c.items[key] return v, ok } c := NewCache[string, int]() c.Set("age", 30) v, ok := c.Get("age") fmt.Println(v, ok) // 30 true
Section 14 — Generics
Q50 — Will this compile? Why?
func Max[T any](a, b T) T {
if a > b {
return a
}
return b
}
[!success]- Answer No — compile error:
invalid operation: a > b (operator > not defined on T)The constraint
anypermits every type, including ones that don't support>. The compiler can't guarantee>is valid. Fix: use a constraint that includes only ordered types:type Ordered interface { ~int | ~float64 | ~string // etc. } func Max[T Ordered](a, b T) T { ... }
Q51 — What's the output?
func Map[T, U any](s []T, f func(T) U) []U {
out := make([]U, len(s))
for i, v := range s {
out[i] = f(v)
}
return out
}
func main() {
nums := []int{1, 2, 3, 4, 5}
strs := Map(nums, func(n int) string {
return fmt.Sprintf("item-%d", n)
})
fmt.Println(strs)
}
[!success]- Answer
[item-1 item-2 item-3 item-4 item-5]Type inference figures out
T=intandU=stringfrom the arguments — no explicit type params needed.Mapbuilds a new[]stringby applying the function to each element.
Q52 — What's the difference?
// A
type Numeric interface {
int | float64
}
// B
type Numeric interface {
~int | ~float64
}
When would A fail but B succeed?
[!success]- Answer With constraint
A, only the exact typesintandfloat64satisfy it.With constraint
B, any type whose underlying type isintorfloat64also satisfies it — including custom types like:type Celsius float64 type UserID int
ArejectsCelsiusandUserID.Baccepts them. Use~Twhenever you want your generic to work with domain types built on top of primitives.
Q53 — Coding Challenge
Write a generic Filter[T any](s []T, keep func(T) bool) []T and a generic Reduce[T, U any](s []T, initial U, fn func(U, T) U) U. Use them to: filter a []int to evens only, then sum the result.
[!success]- Answer
func Filter[T any](s []T, keep func(T) bool) []T { out := make([]T, 0, len(s)) for _, v := range s { if keep(v) { out = append(out, v) } } return out } func Reduce[T, U any](s []T, initial U, fn func(U, T) U) U { acc := initial for _, v := range s { acc = fn(acc, v) } return acc } nums := []int{1, 2, 3, 4, 5, 6, 7, 8} evens := Filter(nums, func(n int) bool { return n%2 == 0 }) sum := Reduce(evens, 0, func(acc, n int) int { return acc + n }) fmt.Println(evens) // [2 4 6 8] fmt.Println(sum) // 20
Section 15 — Enums
Q54 — What does this print?
type Weekday int
const (
Monday Weekday = iota + 1
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
)
func main() {
fmt.Println(int(Monday), int(Wednesday), int(Sunday))
}
[!success]- Answer
1 3 7
iota + 1starts the sequence at 1 (skipping 0). Each subsequent constant increments by 1: Monday=1, Tuesday=2, ..., Sunday=7.
Q55 — What's the output?
type Permission uint
const (
Read Permission = 1 << iota // 1
Write // 2
Execute // 4
)
func main() {
perm := Read | Execute
fmt.Println(perm)
fmt.Println(perm&Write != 0)
fmt.Println(perm&Read != 0)
}
[!success]- Answer
5 false true
Read | Execute=1 | 4=5.perm & Write=5 & 2=0(Write bit not set).perm & Read=5 & 1=1(Read bit is set).
Q56 — What's the bug?
type Direction int
const (
North Direction = iota
East
South
West
)
func describe(d Direction) string {
switch d {
case North: return "north"
case East: return "east"
case South: return "south"
}
return ""
}
func main() {
fmt.Println(describe(West))
fmt.Println(describe(Direction(99)))
}
[!success]- Answer No compile error, but two logic bugs:
Westis not handled —describe(West)silently returns"". Go's compiler does not enforce exhaustive switches on enums.Direction(99)is a valid cast — anyintcan be coerced to your enum type.describereturns""with no indication of the invalid value.Fix: add
case West: return "west"and adefault: panic(...)or error return to catch invalid values.
Q57 — Coding Challenge
Define a Status enum (Pending, Active, Closed) starting from 1. Add a String() method. Write ParseStatus(s string) (Status, error) and demonstrate that fmt.Println uses the String() method automatically.
[!success]- Answer
type Status int const ( Pending Status = iota + 1 Active Closed ) func (s Status) String() string { switch s { case Pending: return "Pending" case Active: return "Active" case Closed: return "Closed" default: return fmt.Sprintf("Status(%d)", int(s)) } } func ParseStatus(s string) (Status, error) { switch s { case "Pending": return Pending, nil case "Active": return Active, nil case "Closed": return Closed, nil default: return 0, fmt.Errorf("unknown status: %q", s) } } func main() { fmt.Println(Active) // Active ← String() called by fmt fmt.Printf("%v\n", Closed) // Closed s, err := ParseStatus("Pending") fmt.Println(s, err) // Pending <nil> _, err = ParseStatus("Unknown") fmt.Println(err) // unknown status: "Unknown" }