LEARNING GO
ตอนที่ 1: Setting Up Your Go Environment
On this page
ตอนที่ 1 ของซีรีส์ Go เราจะตั้งค่า environment ตั้งแต่ศูนย์ — ติดตั้ง Go, เริ่มเขียนโปรแกรมแรก, เรียนรู้เครื่องมือหลักอย่าง go build, go fmt, go vet และสร้าง Makefile เพื่อ automate workflow
สิ่งที่จะได้ตอนจบบทนี้:
- โปรแกรม Hello World ที่ compile เป็น native binary ได้
- เข้าใจ
go build,go fmtและgo vetและใช้ให้เป็นนิสัย Makefileที่รัน workflowfmt → vet → buildใน command เดียว- workflow มาตรฐานที่นำไปใช้กับ project อื่น ๆ ได้
graph TD
A["Source code .go"] -->|go fmt| B["Formatted code"]
B -->|go vet| C{"Static checks pass?"}
C -->|yes| D["go build"]
C -->|no| A
D --> E["Native binary"]
E --> F["Deploy / run"]
Step 1: ติดตั้ง Go
Go toolchain มีตัวติดตั้งสำหรับทุก platform ให้เลือกตามเครื่องที่ใช้
macOS
วิธีที่ 1: Homebrew (แนะนำ)
brew install go
วิธีที่ 2: ดาวน์โหลด installer
ดาวน์โหลดไฟล์ .pkg จาก go.dev/dl ดับเบิลคลิกแล้วทำตามขั้นตอนใน wizard — installer จะติดตั้ง Go และตั้งค่า PATH ให้อัตโนมัติ
Windows
วิธีที่ 1: Chocolatey
choco install golang
วิธีที่ 2: Installer
ดาวน์โหลดไฟล์ .msi จาก go.dev/dl แล้วรัน — installer จะตั้งค่า PATH ให้เช่นกัน
Linux / BSD
ดาวน์โหลด tarball แล้วแตกไฟล์ไปไว้ที่ /usr/local:
# ดาวน์โหลด tarball (เช็ค version ล่าสุดที่ go.dev/dl)
tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz
# เพิ่ม Go ลงใน PATH
echo 'export PATH=$PATH:/usr/local/go/bin' >> $HOME/.bash_profile
source $HOME/.bash_profile
[!CAUTION] ถ้า
tarขึ้น error ให้เติมsudoเพราะการเขียนใน/usr/localต้องใช้สิทธิ์ root
ตรวจสอบการติดตั้ง
เปิด terminal หน้าต่างใหม่ แล้วพิมพ์:
go version
ถ้าติดตั้งสำเร็จจะเห็น output ประมาณนี้:
go version go1.22.0 darwin/arm64
ส่วนประกอบของ output มีดังนี้:
go1.22.0— เวอร์ชันของ Godarwin— kernel ของ macOS (Linux จะเป็นlinux, Windows เป็นwindows)arm64— สถาปัตยกรรม CPU (Apple Silicon =arm64, Intel/AMD =amd64)
ถ้า go version ไม่ทำงาน?
ปัญหามักจะมาจาก:
goไม่อยู่ในPATH— ตรวจสอบด้วยwhich go(macOS/Linux) ถ้าไม่แสดงผลลัพธ์ ให้กลับไปแก้PATH- มีโปรแกรมชื่อ
goอื่นบดบัง —which goจะบอกว่า binary ที่ถูกเรียกอยู่ที่ไหน - ติดตั้งผิด architecture — ลง 64-bit tools บนระบบ 32-bit หรือเลือก chip architecture ไม่ตรงกับเครื่อง (ตรวจสอบด้วย
uname -mบน Linux)
Step 2: สร้าง Go Module
ให้มองว่าโปรเจกต์ Go ทุกโปรเจกต์คือ “module” ซึ่งระบุด้วยไฟล์ go.mod ที่ root ของ project และสร้างด้วยคำสั่ง go mod init:
mkdir hello_world
cd hello_world
go mod init hello_world
Output:
go: creating new go.mod: module hello_world
ไฟล์ go.mod ที่สร้างขึ้นจะมีหน้าตาประมาณนี้:
module hello_world
go 1.22
ไฟล์นี้ทำหน้าที่คล้าย package.json ของ Node.js, requirements.txt ของ Python หรือ Gemfile ของ Ruby โดยระบุชื่อ module, Go version ขั้นต่ำ และ dependencies ของโปรเจกต์
[!WARNING] อย่าแก้
go.modด้วยมือ — ให้ใช้go getและgo mod tidyแทน รายละเอียดจะอยู่ในตอนต่อไปของซีรีส์
Step 3: เขียนโปรแกรมแรก — Hello World
สร้างไฟล์ hello.go ในโฟลเดอร์ hello_world:
package main
import "fmt"
func main() {
fmt.Println("Hello, world!")
}
สังเกตว่า fmt.Println ไม่ได้ indent — ตั้งใจเขียนแบบนี้ เพื่อสาธิตการทำงานของ go fmt ในขั้นถัดไป
โครงสร้างของไฟล์ Go
| ส่วน | คำอธิบาย |
|---|---|
package main | Package declaration — บอกว่าไฟล์นี้อยู่ใน package ชื่อ main ซึ่งเป็น package พิเศษที่มี entry point ของโปรแกรม |
import "fmt" | Import declaration — นำเข้า package fmt สำหรับ format และ print output Go import ได้ทั้ง package เท่านั้น — ไม่สามารถ import แค่ type หรือ function เดียวเหมือน Python |
func main() | Entry point — ทุกโปรแกรม Go เริ่มที่ func main() ใน package main |
fmt.Println(...) | เรียกใช้ function Println จาก package fmt เพื่อแสดงข้อความออกทางหน้าจอ |
Step 4: Compile ด้วย go build
Compile โค้ดให้เป็น native binary ด้วยคำสั่งนี้:
go build
ถ้าไม่มี error จะได้ binary ที่ใช้ชื่อตาม module declaration — ในที่นี้คือ hello_world ลองรันด้วยคำสั่งนี้:
./hello_world
Output:
Hello, world!
ใช้ flag -o เพื่อระบุชื่อ binary เอง
go build -o hello
./hello
go run — รันโดยไม่สร้าง binary file
ถ้าต้องการรันโดยไม่สร้าง binary ค้างไว้ในโฟลเดอร์ ให้ใช้ go run:
go run .
เหมาะกับ script เล็ก ๆ หรือเวลาที่ต้องการทดสอบโค้ดอย่างรวดเร็ว
[!NOTE] Single native binary — Go programs compile เป็น binary เดียวและรันแบบ stand-alone ได้ ไม่ต้องติดตั้ง VM หรือ runtime แยก (ต่างจาก Java, Python และ Node.js) ทำให้ deploy ได้ง่ายมาก สำหรับ container สามารถใช้
scratchหรือdistrolessimage ได้เลย
Step 5: Format โค้ดด้วย go fmt
หนึ่งในการตัดสินใจที่สำคัญที่สุดของ Go คือ กำหนด code format ให้เป็นมาตรฐานเดียวกัน — ไม่มีหลายรูปแบบให้ต้องถกเถียงกัน
Go ใช้ tab indent และวาง { ไว้บรรทัดเดียวกับ declaration — ทุกคนจึงเขียนโค้ดในรูปแบบเดียวกัน
รัน go fmt เพื่อ format ไฟล์ทั้งหมดใน project:
go fmt ./...
Output:
hello.go
เปิด hello.go ดู จะเห็นว่า fmt.Println ถูก indent ด้วย tab ให้อัตโนมัติแล้ว:
package main
import "fmt"
func main() {
fmt.Println("Hello, world!")
}
./... คือการบอกให้ Go ทำงานกับทุก package ในโฟลเดอร์ปัจจุบันและ subdirectory ทั้งหมด — pattern นี้ใช้บ่อยใน Go tooling
ทำไม { ต้องอยู่บรรทัดเดียวกับ func?
Go มีกฎที่เรียกว่า Semicolon Insertion Rule — compiler จะเติม ; ท้ายทุก statement ให้อัตโนมัติ หาก token สุดท้ายก่อน newline เป็น:
- identifier (เช่น
int,float64) - literal (number, string)
- keyword/token:
break,continue,fallthrough,return,++,--,),}
ดังนั้นถ้าเขียนแบบนี้:
// ❌ ผิด — compile ไม่ผ่าน
func main()
{
fmt.Println("Hello, world!")
}
Compiler เห็น ) ท้ายบรรทัด func main() แล้วแทรก ; กลายเป็น:
func main();
{
fmt.Println("Hello, world!");
};
ซึ่งไม่ใช่ Go ที่ถูกต้องตาม syntax — กฎที่เรียบง่ายนี้ทำให้ compiler ทำงานเร็วและช่วยบังคับ coding style ไปในตัว
[!WARNING] อย่าลืม
go fmtก่อน commit — หากลืมแล้วต้องมา format ภายหลัง ให้แยก commit ที่รันgo fmt ./...อย่างเดียวออกจาก logic changes เพื่อให้ diff อ่านง่าย
Step 6: ตรวจสอบโค้ดด้วย go vet
go vet ช่วยหา bug ในโค้ดที่ syntax ถูกต้อง แต่ semantic น่าจะผิด — ตัวอย่างที่ชัดเจนคือ fmt.Printf ที่ template มี placeholder แต่ส่ง argument มาไม่ครบ:
แก้ hello.go เป็น:
package main
import "fmt"
func main() {
fmt.Printf("Hello, %s!\n")
}
โค้ดนี้ compile ผ่านและรันได้ แต่ output จะผิด — ลองรัน go vet:
go vet ./...
Output:
# hello_world
./hello.go:6:2: fmt.Printf format %s reads arg #1, but call has 0 args
go vet จับ bug ให้เราได้ จากนั้นแก้ด้วยการส่ง argument ให้ครบ:
fmt.Printf("Hello, %s!\n", "world")
รัน go vet อีกครั้ง — ถ้าไม่มี output แสดงว่าผ่าน
[!TIP] รัน
go vetทุกครั้งเช่นเดียวกับgo fmt— มันเป็น first line of defense สำหรับหา bug แบบง่าย ๆ ส่วน bug ที่ซับซ้อนกว่านั้นให้ใช้ third-party scanners เพิ่มเติม เช่นstaticcheckและgolangci-lint
Step 7: Automate ทุกอย่างด้วย Makefile
IDE สะดวก แต่ไม่ได้ automate workflow ทั้งหมด — modern software development ต้องมี repeatable build ที่ใครก็รันได้ ไม่ว่าจะอยู่ที่ไหนหรือรันเมื่อใด เพื่อหลีกเลี่ยงปัญหาคลาสสิก “It works on my machine!”
สร้างไฟล์ Makefile ในโฟลเดอร์ hello_world:
.DEFAULT_GOAL := build
.PHONY: fmt vet build
fmt:
go fmt ./...
vet: fmt
go vet ./...
build: vet
go build
[!CAUTION] สำคัญมาก — บรรทัด recipe (คำสั่งใต้ target) ต้อง indent ด้วย tab เท่านั้น หากใช้ space จะ fail และ error ที่ได้อาจไม่ชัดเจน
โครงสร้าง Makefile
| Element | ความหมาย |
|---|---|
fmt, vet, build | Target — operation ที่สั่งได้ |
.DEFAULT_GOAL | target ที่รันเมื่อพิมพ์ make เฉย ๆ (ไม่ระบุ argument) |
vet: fmt | Dependencies — รัน fmt ก่อน แล้วจึงรัน vet |
.PHONY | บอกว่า target เหล่านี้ไม่ใช่ไฟล์จริง เพื่อไม่ให้ make สับสน |
รัน make ครั้งเดียว — จะทำตามลำดับ fmt → vet → build:
make
Output:
go fmt ./...
go vet ./...
go build
จบด้วย command เดียว — ใช้รันทั้ง local และใน CI ได้เหมือนกัน
[!NOTE] บน Windows
makeไม่ได้ติดตั้งมาในตัว ต้องติดตั้งผ่าน Chocolatey ด้วยคำสั่งchoco install make
เลือก IDE
เขียน Go ด้วย text editor และคำสั่ง go ก็ได้ แต่โปรเจกต์ใหญ่ควรใช้ IDE ที่มี autoformat-on-save, code completion และ error reporting แบบ real-time
Visual Studio Code (ฟรี)
VS Code เป็น editor ยอดนิยม — แต่ไม่มี Go support ติดมาให้ ต้องลง Go extension จาก Marketplace เอง
extension นี้จะติดตั้ง third-party tools ให้อัตโนมัติ:
gopls— Go language server ทางการจาก Go team (ให้ code completion, type checking และ find references ขณะพิมพ์)- Delve — Go debugger
[!NOTE] Language Server Protocol (LSP) คือ API spec มาตรฐานที่ทำให้ editor implement code intelligence ได้ โดยไม่ต้องเขียน logic ของแต่ละภาษาไว้ในตัว editor
GoLand (เสียเงิน)
GoLand เป็น IDE จาก JetBrains ที่ทำมาเพื่อ Go โดยเฉพาะ — UI คล้าย IntelliJ/PyCharm มี refactoring, code completion, debugger และ database tools ในตัว ไม่ต้องลง plug-in เพิ่ม
มี 30-day free trial และ Free License Program สำหรับ student หรือ core open source contributor
Go Playground (ออนไลน์)
go.dev/play คือ web-based sandbox — ใช้ทดลอง snippet เล็ก ๆ โดยไม่ต้องติดตั้ง Go ในเครื่อง ปุ่ม Run ใช้รันโค้ด, Format ใช้รัน go fmt และ Share สร้าง unique URL สำหรับแชร์
[!WARNING] ข้อจำกัดของ Playground
- ใช้ network ได้แค่
localhost- process ที่รันนานหรือใช้ memory เยอะอาจถูก kill
- เวลาใน Playground ถูกกำหนดตายตัวไว้ที่ 10 November 2009 (วันเปิดตัว Go)
- ห้ามใส่ข้อมูล sensitive เด็ดขาด — เมื่อกด Share ข้อมูลจะถูกเก็บไว้บน Google servers และใครก็ตามที่มี URL ก็เข้าถึงได้
Go Compatibility Promise
Go ออกรุ่นใหม่ประมาณทุก 6 เดือน พร้อม patch releases สำหรับ bug/security fix ตามความจำเป็น
Go Compatibility Promise สัญญาว่าจะ ไม่ทำ backward-breaking change กับภาษาและ standard library ใน Go 1.x ยกเว้นเมื่อจำเป็นต้องแก้ bug หรือ security issue
“I believe that prioritizing compatibility was the most important design decision that we made in Go 1.” — Russ Cox, GopherCon 2022
[!WARNING] Promise นี้ครอบคลุมเฉพาะ ภาษาและ standard library — ไม่ครอบคลุม
gocommand หาก script CI ใช้ flag ของgocommand ให้ pin Go version และ test ใหม่เมื่อมีการอัปเดต
อัปเดต Go
| Platform | วิธี |
|---|---|
| macOS (brew) | brew upgrade go |
| Windows (choco) | choco upgrade golang |
| Installer | ดาวน์โหลด installer ล่าสุดจาก go.dev/dl แล้วรัน — installer จะลบเวอร์ชันเก่าให้เอง |
| Linux/BSD | ดาวน์โหลด tarball ใหม่ ย้ายของเก่าไปเก็บสำรองก่อน แล้วค่อยลบ |
ตัวอย่างการ upgrade บน Linux:
mv /usr/local/go /usr/local/old-go
tar -C /usr/local -xzf go1.22.1.linux-amd64.tar.gz
rm -rf /usr/local/old-go
แบบฝึกหัด
ลองทำโจทย์ต่อไปนี้โดยสร้าง project ใหม่แยกจาก hello_world เพื่อไม่ให้กระทบตัวอย่างในบท:
- สร้าง module ชื่อ
greetingsพร้อมไฟล์main.goที่ printHello from Go!แล้วรันด้วยgo run . - เขียน
fmt.Printf("Hello, %s!\n")ที่ลืมส่ง argument แล้วรันgo vet ./...— สังเกต error ที่เจอ จากนั้นแก้ให้ผ่าน - Compile ด้วย
go build -o helloจากนั้นกลับไปยังโฟลเดอร์แม่ แล้วรัน binary จากไดเรกทอรีอื่นด้วย path เช่น./hello_world/hello - เพิ่ม target
runและcleanใน Makefile ของhello_worldโดยrunรันgo run .และcleanลบ binary ทิ้ง — อย่าลืมเพิ่มทั้งสองใน.PHONY - ทดลองเขียน
{คนละบรรทัดกับfuncแล้วรันgo build— อ่าน error ที่ได้ แล้วแก้กลับตาม semicolon insertion rule
ตรวจโค้ดหลังทำเสร็จ:
go fmt ./...
go vet ./...
go build
Common Pitfalls — ข้อผิดพลาดที่พบบ่อย
- วาง
{คนละบรรทัดกับfunc/if/for— semicolon insertion rule จะทำให้ compile ไม่ผ่านแม้ logic จะถูกต้อง - ลืม
go fmtก่อน commit — diff ภายหลังจะเต็มไปด้วย whitespace change จนบดบัง logic change จริง - แก้
go.modด้วยมือ — ทำให้ state ของ module ไม่ตรงกับ tool ให้ใช้go get/go mod tidyแทน - เชื่อคำแนะนำเก่าเรื่อง
GOROOT/GOPATH— module system สมัยใหม่ไม่จำเป็นต้องใช้แล้ว วาง project ไว้ที่ไหนก็ได้ - Indent
Makefileด้วย space — Makefile รับเฉพาะ tab หากใช้ space จะ fail และ error ที่ได้อาจไม่ชัดเจน
สรุป
ในบทนี้เราได้:
- ✅ ติดตั้ง Go toolchain และตรวจสอบว่าใช้งานได้
- ✅ สร้าง Go module ด้วย
go mod init - ✅ เขียนโปรแกรมแรก — Hello World
- ✅ Compile เป็น native binary ด้วย
go build - ✅ Format โค้ดด้วย
go fmtและเข้าใจ semicolon insertion rule - ✅ ตรวจสอบ bug ด้วย
go vet - ✅ สร้าง
Makefileเพื่อ automate workflowfmt → vet → build
Workflow fmt → vet → build นี้คือ heartbeat ของ Go development — ใช้ได้กับทุก project ไม่ว่าจะใหญ่หรือเล็ก
ใน ตอนต่อไป เราจะเจาะลึก primitive types และการประกาศตัวแปรของ Go — ตัวแปร, constants และ type พื้นฐานต่าง ๆ
Glossary
- Toolchain — ชุดเครื่องมือทั้งหมดที่ใช้พัฒนาด้วย Go เช่น compiler,
gocommand และ standard library - Module — หน่วยการจัดการ dependency ของ Go ระบุด้วยไฟล์
go.mod - Package — กลุ่มไฟล์ Go ที่อยู่ร่วมกัน ประกาศด้วยคำสั่ง
package - Entry point — จุดเริ่มต้นของโปรแกรม; ใน Go คือ
func main()ในpackage main - Native binary — ไฟล์ executable ที่ compile เป็นภาษาเครื่องแล้ว รันได้โดยไม่ต้องมี runtime แยก
- Semicolon Insertion Rule — กฎที่ compiler เติม
;ท้าย statement ให้อัตโนมัติ - LSP (Language Server Protocol) — มาตรฐานที่ให้ editor ทำงานร่วมกับภาษาใดก็ได้ผ่าน language server
- Dependency — package ภายนอกที่โปรแกรมใช้งาน
- CI (Continuous Integration) — ระบบรัน build และตรวจสอบโค้ดอัตโนมัติทุกครั้งที่ push
- Go Compatibility Promise — สัญญาว่า Go 1.x จะไม่ทำ breaking change กับภาษาและ standard library
Related
- ตอนที่ 2: Predeclared Types and Declarations — ชนิดข้อมูลพื้นฐานและการประกาศตัวแปรของ Go ก้าวต่อไปของซีรีส์