บทเรียน 02 · Day 1 · Variables & Types

var, := และเรื่อง type ที่ Go ไม่ยอมให้ผ่าน

~12 นาที · ต้องมี: บทเรียน 01

Go มีวิธีประกาศตัวแปร 3 แบบ และมีที่ที่ใช้ได้ไม่เหมือนกัน — ที่สำคัญกว่านั้นคือ Go ไม่ยอมแปลง type ให้อัตโนมัติแม้แต่นิดเดียว บวก int กับ float64 ตรง ๆ ก็ไม่ผ่าน บทนี้จะทำให้คุณเลิกงงกับ error ประเภท mismatched types ตลอดไป

1 · สามวิธีประกาศตัวแปร

// 1) var + type — ไม่ใส่ค่า ได้ zero value
var i int                        // i = 0

// 2) var + ค่าเริ่มต้น — เขียน type ไว้ก็ได้ หรือละไว้ให้ compiler เดาจากค่าก็ได้
var j int = 2                // เขียน type ไว้ชัด ๆ
var c, python, java = true, false, "no!"   // ละ type — compiler เดาให้เป็น bool, bool, string

// 3) short declaration — สั้นสุด ใช้บ่อยสุด
k := 3
name, age := "Alice", 30
ข้อจำกัดที่ต้องจำ

:= ใช้ได้เฉพาะข้างในฟังก์ชัน — นอกฟังก์ชัน (ระดับ package) ต้องใช้ var เท่านั้น
เหตุผลคือทุกบรรทัดที่อยู่นอกฟังก์ชันต้องขึ้นต้นด้วย keyword (คำสงวนที่ภาษาจองไว้ เอาไปตั้งเป็นชื่อตัวแปรไม่ได้ — Go มีทั้งหมด 25 คำ) และ keyword ที่ขึ้นต้นบรรทัดระดับ package ได้มีแค่ 6 คำ: package, import, func, var, const, type — และ := ไม่ใช่ keyword

สถานการณ์ใช้อะไร
ในฟังก์ชัน มีค่าเริ่มต้นอยู่แล้ว:= — สำนวนมาตรฐาน
ต้องการ zero value (ค่าเริ่มต้นที่ Go เติมให้เอง — หัวข้อ 2)var x int
นอกฟังก์ชัน (package level)var เท่านั้น
อยากบังคับ type ให้ชัดvar x float64 = 3
🗣 ศัพท์ที่เพิ่งเรียนไป
keyword
คำสงวนที่ภาษาจองไว้ เอาไปตั้งเป็นชื่อตัวแปรไม่ได้ · Go มี 25 คำ (break, func, if, for, var ฯลฯ) ซึ่งถือว่าน้อยมากเมื่อเทียบกับภาษาอื่น — ประโยคที่เอาไปพูดได้เลยคือ “Go มี keyword แค่ 25 คำ”
package level
“ตำแหน่งที่อยู่นอกฟังก์ชัน” · เวลาอ่าน error หรือเอกสารจะเจอคำนี้บ่อย เช่น “ตัวแปรตัวนี้ประกาศไว้ที่ package level” = ประกาศไว้นอกฟังก์ชัน ทุกฟังก์ชันในแพ็กเกจเดียวกันเห็นหมด
short variable declaration (:=)
ชื่อทางการของ := · ใช้ได้เฉพาะในฟังก์ชัน · ถ้าเผลอเขียนนอกฟังก์ชันจะเจอ error ว่า syntax error: non-declaration statement outside function body — เจอข้อความนี้เมื่อไหร่ให้นึกถึงคำนี้ทันที

2 · Zero value — Go ไม่มีตัวแปร "ไม่มีค่า"

ประกาศแล้วไม่ใส่ค่า Go จะเติมค่าให้เสมอ — ไม่มีคำว่า undefined

TypeZero value
int, float64 และตัวเลขทุกชนิด0
boolfalse
string"" (สตริงว่าง ไม่ใช่ null)
pointer, slice, map, func, chan, interface (ยังไม่ต้องรู้ว่าคืออะไร จะเจอตัวจริงในบทหลัง ๆ)nil — คำที่ Go ใช้แทน "ยังว่าง ยังไม่ได้ชี้ไปที่อะไร" คล้าย null ในภาษาอื่น

ตัวอย่างข้างล่างใช้ fmt.Printf ซึ่งต่างจาก fmt.Println ที่เคยเห็นในบทที่ 1 — Printf ต้องส่ง "สตริงรูปแบบ" เข้าไปเป็นตัวแรก แล้วเอาค่าที่ตามมาไปเสียบแทนตำแหน่งที่ ขึ้นต้นด้วย % เรียงตามลำดับ ตอนนี้รู้แค่ 2 ตัวพอ: %v = พิมพ์ค่านั้นแบบปกติ, %q = พิมพ์สตริงพร้อมเครื่องหมายคำพูดครอบ (จะได้เห็นชัดว่าสตริงว่างหน้าตาเป็นยังไง) ส่วน \n คือสั่งขึ้นบรรทัดใหม่ — รายการ % ทั้งหมดอยู่ในหัวข้อ 4

var i int; var f float64; var b bool; var s string
fmt.Printf("%v %v %v %q\n", i, f, b, s)
// 0 0 false ""

ทำไมเรื่องนี้สำคัญ: ในภาษาอื่น "ตัวแปรยังไม่ได้เซ็ต" กับ "ตัวแปรเป็นศูนย์" ต่างกัน แต่ Go ยุบให้เหลืออันเดียว — ตัวแปรพร้อมใช้เสมอ ไม่ต้องเช็ค null ก่อน แลกกับที่คุณต้องระวังว่า "0" อาจแปลว่า "ยังไม่ได้ตั้งค่า" ก็ได้

🥤 เทียบให้เห็นภาพ — แก้วเปล่าที่วางไว้ให้แล้ว

ร้านอาหารบางร้านวางแก้วเปล่าไว้ที่โต๊ะตั้งแต่ก่อนเรานั่ง อยากดื่มน้ำก็รินได้เลย ไม่ต้องเรียกพนักงานมาเอาแก้วให้ก่อน
อีกร้านไม่วางอะไรไว้เลย — ต้องเช็คทุกครั้งว่าตรงหน้ามีแก้วหรือยัง ถ้าลืมเช็คแล้วรินเลย น้ำก็หกเลอะโต๊ะ

Go เป็นร้านแบบแรก: ประกาศตัวแปรปุ๊บ มีค่าให้ใช้ทันที
ส่วนภาษาที่มี null หรือ undefined เป็นร้านแบบหลัง — ต้องเช็คก่อนใช้ ไม่งั้นโปรแกรมพัง

ที่ไม่เหมือนกัน: แก้วเปล่ากับแก้วที่คนดื่มหมดพอดี หน้าตาเหมือนกันเป๊ะ · ตัวแปรก็เหมือนกัน เห็นเลข 0 แล้วบอกไม่ได้ว่า "ยังไม่มีใครใส่ค่า" หรือ "ใส่ค่าเป็นศูนย์มาจริง ๆ" ตรงนี้เป็นเรื่องที่เราต้องออกแบบเองว่าจะแยกยังไง
และไม่ใช่ทุกแก้วจะรินได้เลย — pointer, slice, map ฯลฯ มี zero value เป็น nil ซึ่งเท่ากับ "ยังไม่มีแก้ว" เขียนลง map ที่เป็น nil จะพังทันที (panic: assignment to entry in nil map) · กลุ่มนี้ยังต้องเช็คก่อนใช้อยู่ ที่ไม่ต้องเช็คเลยคือตัวเลข สตริง และ bool

🗣 ศัพท์ที่เพิ่งเรียนไป
zero value
“ค่าที่ Go เติมให้เองตอนประกาศ” · เวลาถามคนอื่นให้พูดว่า “zero value ของ type นี้คืออะไร” จะได้คำตอบตรงกว่าถามว่า “ตัวแปรนี้เริ่มที่เท่าไหร่”
nil
คำที่ Go ใช้แทน “ยังว่าง ยังไม่ได้ชี้ไปที่อะไร” · ประโยคที่จะได้ยินบ่อยคือ “ตัวนี้เป็น nil อยู่” = ยังไม่มีของจริงอยู่ข้างใน · ระวังไว้ว่า int, string, bool เป็น nil ไม่ได้เลย
nil check
การเช็คก่อนใช้ว่ามีของจริงไหม — เขียนว่า if x != nil · ข้อดีของ zero value คือ ตัวเลข สตริง และ bool ไม่ต้อง nil check เลย ประโยคนี้เอาไปพูดกับ senior ได้ตรง ๆ

3 · จุดที่คนใหม่ติดกันทุกคน: Go ไม่แปลง type ให้

var i int = 42
var f float64 = 1.5

result := i + f

โค้ดนี้เป็นยังไง?

compile ไม่ผ่าน:

invalid operation: i + f (mismatched types int and float64)

ในภาษาอย่าง Python หรือ JavaScript อันนี้ผ่านสบาย ๆ แต่ Go บังคับให้ คุณเขียนออกมาเองว่าจะแปลงยังไง ต้องเขียน float64(i) + f

รูปแบบการแปลงคือ T(v) — เอา type มาครอบค่า:

i := 42
f := float64(i)     // int → float64
u := uint(f)        // float64 → uint

// ใช้จริง — math.Sqrt รับ float64 เท่านั้น
var x, y int = 3, 4
var dist float64 = math.Sqrt(float64(x*x + y*y))
ระวังข้อมูลหาย
f := 3.9
i := int(f)      // 3 — ตัดทิ้ง ไม่ใช่ปัดเศษ!

big := 300
b := uint8(big) // 44 — uint8 เก็บได้แค่ 0–255 ล้นแล้ววนกลับ

การแปลง type ไม่มีการเตือน ถ้าค่าไม่พอดี

💱 เทียบให้เห็นภาพ — บูธแลกเงินที่สนามบิน

มีแบงก์ไทยอยู่ในกระเป๋าซ้าย มีเงินเยนอยู่กระเป๋าขวา อยากรู้ว่ารวมกันได้เท่าไหร่
เทกองรวมกันแล้วนับเลยไม่ได้ — ต้องเดินไปบูธแลกเงินก่อน ให้ทั้งสองกองอยู่ในสกุลเดียวกัน แล้วค่อยบวก

int กับ float64 ก็คนละสกุลกัน · float64(i) คือการเดินไปบูธ — พูดออกมาชัด ๆ ว่าจะแปลงตัวไหนเป็นอะไร
Go ไม่มีบูธอัตโนมัติที่แลกให้เงียบ ๆ แบบ Python หรือ JavaScript เราต้องเขียนสั่งเอง — กฎนี้ใช้กับค่าที่อยู่ในตัวแปร ส่วนค่าคงที่ที่เขียนตรง ๆ Go ยอมให้เอง เช่น var x float64 = 3 (ดูคำว่า untyped constant ข้างล่าง)

ที่ไม่เหมือนกัน: บูธแลกเงินจริงเปลี่ยนตัวเลขบนแบงก์ตามอัตราแลกเปลี่ยน แต่ float64(42) ยังเป็น 42 เท่าเดิม เปลี่ยนแค่ "ชนิด" ที่ Go มองเห็น · ค่าจะเพี้ยนก็ต่อเมื่อปลายทางเก็บไม่ไหวเท่านั้น เช่น f := 3.9 แล้วแปลง int(f) จะได้ 3 เพราะ int ไม่มีที่ให้เก็บทศนิยม

แต่ถ้าเป็นค่าคงที่ Go จะจับให้ตั้งแต่ compile
// ผ่านตัวแปร → รันได้ ตัดค่าเงียบ ๆ
f := 3.9;   fmt.Println(int(f))       // 3   ✓ รันได้
big := 300; fmt.Println(uint8(big))  // 44  ✓ รันได้

// ใส่ค่าคงที่ตรง ๆ → compile ไม่ผ่าน!
fmt.Println(int(3.9))             // ✗ cannot convert 3.9 (untyped float constant) to type int
fmt.Println(uint8(300))           // ✗ constant 300 overflows uint8

เพราะ ค่าคงที่ (constant) Go รู้ค่าตั้งแต่ตอน compile เลยตรวจได้เลยว่า ข้อมูลจะหาย — แต่ค่าในตัวแปรรู้ตอน runtime เท่านั้น จึงได้แค่ปล่อยผ่าน นี่คือเหตุผลที่ต้องระวังตัวแปร มากกว่าค่าคงที่

🗣 ศัพท์ที่เพิ่งเรียนไป
type conversion
การแปลงชนิดที่เราเขียนสั่งเอง = T(v) · เวลาติดให้ถามว่า “ตรงนี้ต้อง convert เป็น float64 ก่อนไหม” แทนที่จะถามว่า “ทำไมมันบวกกันไม่ได้” — จะได้คำตอบเร็วกว่ามาก
implicit / explicit conversion
implicit = ภาษาแปลงให้เงียบ ๆ (Python, JavaScript) · explicit = เราต้องเขียนเอง (Go) · ประโยคที่เอาไปใช้ได้เลย: “Go ไม่มี implicit conversion” ประโยคเดียวนี้อธิบายหัวข้อนี้ได้ทั้งหัวข้อ
mismatched types
ข้อความ error ที่แปลว่า “สองข้างคนละชนิด” · เอาไปค้นหรือถาม AI ได้ตรง ๆ ว่า “invalid operation: mismatched types int and float64 แก้ยังไง” ดีกว่าพิมพ์ว่า “โค้ด Go บวกเลขไม่ได้”
overflow
“ใส่ค่าเกินที่ภาชนะรับไหว แล้ววนกลับ” เช่น big := 300 แล้วแปลง uint8(big) จะได้ 44 · เวลาสงสัยว่าตัวเลขจะล้นไหม ให้ถามว่า “ตรงนี้มีโอกาส overflow ไหม”
untyped constant
ค่าคงที่ที่เขียนลงไปตรง ๆ อย่าง 3.9 หรือ 300 ซึ่ง ยังไม่มีชนิดจนกว่าจะถูกเอาไปใช้ · นี่คือเหตุผลที่ Go จับ int(3.9) ได้ตั้งแต่ตอนแปลโค้ด แต่จับ int(f) ไม่ได้
compile time / runtime
compile time = ตอนแปลโค้ด ยังไม่ทันรัน · runtime = ตอนโปรแกรมทำงานจริง · “พังตอน compile” ดีกว่า “พังตอน runtime” เสมอ เพราะยังไม่ทันถึงมือผู้ใช้

4 · Type inference — compiler เดา type ให้ยังไง

n := 42
f := 3.142
s := "hello"
b := true

fmt.Printf("%T %T %T %T\n", n, f, s, b)

พิมพ์ type อะไรออกมาบ้าง?

int float64 string bool

สังเกตว่า 3.142 ได้ float64 ไม่ใช่ float32 และ 42 ได้ int ไม่ใช่ int64 — นี่คือ default type ของค่าคงที่ที่ยังไม่ระบุชนิด

%T คือตัวช่วยที่พิมพ์ type ออกมา — เครื่องมือดีบักที่ดีที่สุด ตอนงงว่าตัวแปรเป็น type อะไร

🗣 ศัพท์ที่เพิ่งเรียนไป
format verb
โค้ดสั้น ๆ ที่ขึ้นต้นด้วย % ใช้บอก Printf ว่าจะพิมพ์ค่านั้นออกมาหน้าตาแบบไหน เช่น %v, %T · ตารางข้างล่างคือชุดที่ใช้บ่อยที่สุด
type inference
“compiler เดา type จากค่าที่เราให้มา” — คือสิ่งที่เกิดขึ้นตอนเขียน n := 42 · เวลาถาม senior ให้พูดว่า “ตัวนี้ inference ออกมาเป็น type อะไร” จะสื่อกว่าถามว่า “ตัวนี้เป็นเลขแบบไหน”
default type
ชนิดที่ค่าคงที่จะกลายเป็นถ้าเราไม่ระบุ (เลขจำนวนเต็ม → int, เลขทศนิยม → float64) · นี่คือเหตุผลที่ f := 3.142 ได้ float64 ไม่ใช่ float32
verbพิมพ์อะไรตัวอย่างผลลัพธ์
%vค่าแบบ default — ใช้ได้กับทุก type42 / hi / true
%Ttype ของค่านั้นint / string / float64
%d / %s / %tเลขจำนวนเต็ม / สตริง / bool42 / hi / true
%qสตริงพร้อมเครื่องหมายคำพูด"hi"
%.2fทศนิยม 2 ตำแหน่ง2500.00
%+vค่า + ชื่อ field (ใช้กับ struct — เรียนบทหลัง){Name:Alice Age:30}

5 · เลือก type ตัวเลขยังไง

กฎง่าย ๆ ที่ใช้ได้ 95% ของเวลา

ใช้ int กับ float64 ไปเลย จนกว่าจะมีเหตุผลเฉพาะที่ต้องใช้อย่างอื่น

แล้วเหตุผลเฉพาะคืออะไรบ้าง:

Typeช่วงค่าใช้ตอน
int8−128 ถึง 127protocol, binary data, ประหยัด memory
int32±2.1 พันล้านfield ขนาดคงที่, Unicode rune
int64±9.22e18timestamp, database ID, ขนาดไฟล์
uint8 (byte)0 ถึง 255ข้อมูล byte, ช่องสี, raw binary
rune= int32ตัวอักษร 1 ตัว (สำคัญมากกับภาษาไทย)
byte vs rune — เรื่องที่คนไทยเจอแน่นอน
s := "สวัสดี"
len(s)              // 18 — นับเป็น byte!
len([]rune(s))      // 6  — นับเป็น rune ✓

[]rune(s) อ่านว่า "แปลงสตริง s เป็นลิสต์ของ rune" — เครื่องหมาย [] ที่นำหน้า type แปลว่า "ลิสต์ของสิ่งนั้น" (ชื่อจริงคือ slice จะเรียนเต็ม ๆ ในบทเรื่อง slice) พอกลายเป็นลิสต์ของ rune แล้ว len ก็นับจำนวนสมาชิกในลิสต์ แทนที่จะนับ byte

ตัวอักษรไทยตัวหนึ่งกิน 3 bytes ใน UTF-8 → len เลยได้ 18 ไม่ใช่ 6
แต่ระวังคำว่า "6" ให้ดี — มันคือจำนวน rune ไม่ใช่จำนวนตัวที่ตามองเห็น: ส · ว · ◌ั · ส · ด · ◌ี คือ 6 rune เพราะสระและวรรณยุกต์นับเป็น rune ของตัวเอง ทั้งที่เขียนซ้อนอยู่บนพยัญชนะ
สรุป: ถ้าจะนับ "ตัวอักษร" ให้ใกล้เคียงความจริง ต้องแปลงเป็น []rune ก่อนเสมอ ส่วนการนับ "ตัวที่ตามองเห็น" ของภาษาไทยยังต้องใช้วิธีอื่นอีก ไว้พูดถึงในบทเรื่อง string

🔐 เทียบให้เห็นภาพ — ตู้ล็อกเกอร์ที่สถานีรถไฟ

ลองนึกถึงตู้ล็อกเกอร์ที่ทุกช่องขนาดเท่ากันหมด ของชิ้นเล็กใส่ช่องเดียวจบ แต่ของชิ้นใหญ่ต้องจองติดกัน 3 ช่องถึงจะใส่ได้
พอถามว่า "ใช้ไปเท่าไหร่" คำตอบจะได้คนละเลข ขึ้นกับว่านับอะไร: นับช่องที่ใช้ กับนับจำนวนของ

len(s) คือการนับ "ช่อง" (byte) · len([]rune(s)) คือการนับ "ชิ้นของ" (rune)
ตัวอักษรอังกฤษเป็นของชิ้นเล็ก 1 ช่องจบ · ตัวอักษรไทยเป็นของชิ้นใหญ่ กิน 3 ช่อง → "สวัสดี" จึงใช้ไป 18 ช่อง ทั้งที่มีของอยู่ 6 ชิ้น

ที่ไม่เหมือนกัน: ของในล็อกเกอร์คือ "1 ชิ้นที่ตามองเห็น" แต่ rune ไม่ใช่ — สระกับวรรณยุกต์ที่ลอยอยู่บนพยัญชนะนับเป็นชิ้นแยกของมันเอง "สวัสดี" จึงเป็น 6 rune ทั้งที่ตาเห็นเป็น 4 ตัว (ส · วั · ส · ดี)

🗣 ศัพท์ที่เพิ่งเรียนไป
byte
“1 ช่องเก็บ” — หน่วยเล็กสุดที่ len ของ string นับให้ · ใน Go คำว่า byte เป็นแค่ชื่อเล่นของ uint8
rune
“Unicode code point 1 ตัว” — คำว่า code point คือศัพท์ทางการ ใช้คำนี้เวลาคุยกับ senior หรือค้นเอกสาร · ใน Go เป็นชื่อเล่นของ int32 · ระวังว่า 1 code point ยังไม่เท่ากับ 1 ตัวที่ตามองเห็น (อันนั้นคือ grapheme cluster ข้างล่าง) · เวลาถามให้พูดว่า “ตรงนี้นับเป็น byte หรือ rune” จะสื่อกว่าถามว่า “ทำไมมันนับตัวอักษรผิด”
UTF-8 (encoding)
วิธีเข้ารหัสที่กำหนดว่าตัวอักษรแต่ละตัวจะกินกี่ byte — อังกฤษ 1 byte ส่วนไทยส่วนใหญ่ 3 bytes · string literal ที่เราพิมพ์ลงในโค้ด Go เป็น UTF-8 เสมอ ส่วนตัว string เองคือลำดับ byte อะไรก็ได้ (เช่น byte ดิบที่อ่านมาจากไฟล์ อาจไม่ใช่ UTF-8 ที่ถูกต้อง) แต่โดยธรรมเนียมแล้วเราเก็บเป็น UTF-8
grapheme cluster
“ตัวที่ตามองเห็น 1 ตัว” ซึ่งอาจประกอบจากหลาย rune (พยัญชนะ + สระ + วรรณยุกต์) · ถ้าวันไหนต้องนับตัวอักษรไทยให้ตรงกับที่คนเห็น คำที่ต้องเอาไปค้นคือคำนี้ ไม่ใช่ rune

6 · ทดสอบความจำ

1. เขียน count := 0 ไว้นอกฟังก์ชัน (ระดับ package) จะเป็นยังไง?

syntax error: non-declaration statement outside function body — := ใช้ได้เฉพาะในฟังก์ชัน นอกฟังก์ชันทุกบรรทัดต้องขึ้นต้นด้วย keyword

2. var s string แล้วยังไม่ใส่ค่า — s มีค่าอะไร?

zero value ของ string คือ "" — ไม่ใช่ nil (string ใน Go เป็น nil ไม่ได้เลย) ใช้ได้ทันทีไม่ต้องเช็คก่อน

3. i เป็น int, f เป็น float64 — เขียนยังไงถึงบวกกันได้?

Go ใช้รูปแบบ T(v) — เอา type ครอบค่า ไม่ใช่แบบ C ((float64) i) และไม่ใช่ type assertion (i.(float64) ซึ่งใช้กับ interface เท่านั้น)

4. len("สวัสดี") คืนค่าอะไร?

len() ของ string นับ bytes เสมอ — ตัวอักษรไทยใน UTF-8 ใช้ 3 bytes ต่อ rune และ "สวัสดี" มี 6 rune (ส · ว · ◌ั · ส · ด · ◌ี — สระนับแยกด้วย) → 6 × 3 = 18

ตัวเลือกอื่นผิดเพราะ: 6 คือจำนวน rune ไม่ใช่สิ่งที่ len คืนให้ · 3 ไม่ใช่ผลของอะไรเลย Go ไม่มีแนวคิดเรื่องพยางค์ · 0 ก็ไม่ใช่ เพราะ Go เก็บ string เป็น UTF-8 อยู่แล้ว รองรับไทยเต็มที่

อยากได้ 6 ต้องเขียน len([]rune("สวัสดี")) หรือ utf8.RuneCountInString(s)

7 · ลงมือทำ

ภารกิจ: ทำให้ compiler ด่าแล้วซ่อมให้ได้

  1. ใน Playground ประกาศตัวแปรทั้ง 3 แบบ อย่างละ 1 ตัว แล้วพิมพ์ทั้งค่าและ type ด้วย fmt.Printf("%v %T\n", x, x)
  2. ประกาศ var i int = 42 และ var f float64 = 1.5 แล้วลอง i + f — อ่าน error ให้ครบทั้งบรรทัด แล้วซ่อม
  3. เก็บใส่ตัวแปรก่อน แล้วค่อยแปลง: f := 3.9; fmt.Println(int(f)) และ big := 300; fmt.Println(uint8(big)) — ทำนายผลก่อนรัน แล้วดูว่าตรงไหม
  4. ทีนี้ลองแปลง ค่าคงที่ตรง ๆ: int(3.9) และ uint8(300) — คนละเรื่องกับข้อ 3 นะ อ่าน error แล้วอธิบายให้ได้ว่าทำไม Go ถึงยอมให้ทำข้อ 3 แต่ไม่ยอมข้อนี้
  5. ประกาศตัวแปร 1 ตัวแล้วไม่ใช้เลย — จำ error declared and not used ไว้ให้ขึ้นใจ
  6. พิมพ์ len("สวัสดี") เทียบกับ len([]rune("สวัสดี"))
  7. ท้าทาย: เขียนโปรแกรมที่รับ completedTopics, totalTopics เป็น int แล้วพิมพ์เปอร์เซ็นต์ทศนิยม 2 ตำแหน่ง — คำใบ้: int / int ได้ int (5/2 = 2 ไม่ใช่ 2.5) ต้องแปลงก่อนหาร
ชัยชนะของบทนี้

เจอ error mismatched types หรือ cannot use x (type A) as type B คุณจะไม่งงอีก — รู้เลยว่าต้องหาจุดที่ต้องเขียน T(v) ครอบ

อ่านต่อจากต้นทาง

แหล่งหลักที่แนะนำ: A Tour of Go — Basic types (หน้า 8–14) กดรันแก้โค้ดได้ในหน้าเว็บเลย ทำให้ครบทั้ง 7 หน้า ใช้เวลาไม่เกิน 15 นาที และครอบคลุมทุกอย่างในบทนี้

💬 ติดตรงไหนถามได้เลย — ข้อ 7 ของภารกิจ (หาร int แล้วได้ int) เป็นบั๊กที่เจอในงานจริงบ่อยมาก ถ้าทำแล้วผลออกมาเป็น 0 หรือ 100 เป๊ะ ๆ แปลว่าเจอกับดักแล้ว ถามมาได้

อ้างอิง