ใน Golang, nil
คือตัวระบุที่กำหนดไว้ล่วงหน้าซึ่งมีความหมายแตกต่างกันในบริบทต่างๆ แต่โดยทั่วไปแล้วจะแทน "ไม่มี", "ว่างเปล่า" หรือ "ค่าศูนย์" สามารถกำหนดให้กับตัวแปรประเภทตัวชี้, slice, map, channel, function และ interface การทำความเข้าใจความสำคัญของ nil นั้นมีความสำคัญอย่างยิ่งต่อการเขียนโปรแกรม Go ที่แข็งแกร่ง เนื่องจากการจัดการ nil ที่ไม่ถูกต้องอาจนำไปสู่ปัญหาที่ไม่คาดคิด
nil ในตัวชี้
ใน Go ตัวชี้เป็นชนิดข้อมูลพื้นฐานที่เก็บที่อยู่หน่วยความจำของตัวแปร เมื่อประกาศตัวชี้แต่ไม่ได้เริ่มต้นค่า ค่าของมันจะเป็น nil
ตัวอย่างโค้ดต่อไปนี้แสดงให้เห็น
package main
import "fmt"
func main() {
var ptr *int
fmt.Println(ptr == nil) // true
}
ถ้าคุณอ้างอิงตัวชี้ nil
มันจะส่งผลให้เกิด panic ดังนั้นจึงมีความสำคัญอย่างยิ่งที่จะต้องตรวจสอบว่าตัวชี้เป็น nil
หรือไม่ก่อนที่จะดำเนินการใดๆ กับตัวชี้
nil ใน Slices
Slice คืออาร์เรย์แบบไดนามิกประกอบด้วยอาร์เรย์พื้นฐานและชุดข้อมูลที่อธิบายคุณสมบัติของ slice เมื่อประกาศ slice แต่ไม่ได้เริ่มต้นค่า ค่าของมันจะเป็น nil
ตัวอย่างโค้ดต่อไปนี้แสดงให้เห็น
package main
import "fmt"
func main() {
var s []int
fmt.Println(s == nil) // true
}
Slice nil
ไม่ชี้ไปยังอาร์เรย์พื้นฐานที่ถูกต้อง และทั้งความยาว (len) และความจุ (cap) เป็น 0 อย่างไรก็ตาม slice nil
และ slice ว่าง (สร้างด้วย make([]int, 0) หรือ []int{}) นั้นแตกต่างกัน Slice nil ไม่ใช้หน่วยความจำจนกว่าจะมีการจัดสรรพื้นที่ให้ ในขณะที่ slice ว่าง แม้ว่าจะมีความยาวเป็น 0 แต่ก็มีตัวชี้ชี้ไปยังอาร์เรย์พื้นฐานที่มีความยาว 0 อยู่แล้ว
nil ใน Maps
Map ใช้เพื่อเก็บคอลเลกชันของคู่คีย์-ค่า โดยที่คีย์นั้นไม่ซ้ำกัน เมื่อประกาศ map แต่ไม่ได้เริ่มต้นค่า ค่าของมันจะเป็น nil
ซึ่งหมายความว่ายังไม่มีการจัดสรรพื้นที่หน่วยความจำ และไม่สามารถใช้งานได้โดยตรง ตัวอย่างโค้ดต่อไปนี้แสดงให้เห็น
package main
import "fmt"
func main() {
var myMap map[string]int
fmt.Println(myMap == nil)
}
การเขียนข้อมูลลงใน map nil
จะส่งผลให้เกิด panic เนื่องจาก map nil
ไม่มีโครงสร้างข้อมูลพื้นฐานในการเก็บข้อมูล อย่างไรก็ตาม การอ่านข้อมูลจาก map nil
จะไม่ทำให้เกิดข้อผิดพลาด มันจะส่งคืนค่าศูนย์สำหรับชนิดข้อมูลที่เกี่ยวข้องเท่านั้น
Map nil
และ map ที่ไม่มีคู่คีย์-ค่า (map ว่าง) นั้นแตกต่างกัน Map nil
ไม่สามารถใช้เพื่อเก็บคู่คีย์-ค่าได้ ในขณะที่ map ว่างได้รับการเริ่มต้นแล้วแต่ไม่มีองค์ประกอบ ตัวอย่างเช่น:
// nil map
var nilMap map[string]int
// e,pty map
emptyMap := make(map[string]int)
คุณสามารถจัดการ map ว่าง เช่น การเพิ่มหรือลบคู่คีย์-ค่าได้ อย่างไรก็ตาม การดำเนินการเหล่านี้กับ map nil จะส่งผลให้เกิด panic
nil ใน Channels
Channels เป็น primitive สำหรับการซิงโครไนซ์ใน Go ใช้สำหรับส่งข้อความระหว่าง Go routines (goroutines) เมื่อประกาศ channel แต่ไม่ได้เริ่มต้นค่า ค่าของมันจะเป็น nil
ตัวอย่างโค้ดต่อไปนี้แสดงให้เห็น
package main
import "fmt"
func main() {
var ch chan int
fmt.Println(ch == nil) // true
}
การพยายามส่งหรือรับข้อมูลบน channel nil
จะบล็อกอย่างไม่มีกำหนด เนื่องจาก channel nil
ไม่ได้ถูกปิดและไม่มี goroutine อื่นที่จะดำเนินการส่งหรือรับ อย่างไรก็ตาม channel nil
มีวัตถุประสงค์พิเศษในคำสั่ง select และสามารถใช้เพื่อปิดใช้งานสาขาเฉพาะในคำสั่ง select ได้
nil ใน Functions
ใน Go ฟังก์ชันก็เป็นชนิดข้อมูลเช่นกัน และคุณสามารถใช้ nil
เพื่อแทนฟังก์ชันที่ไม่ได้เริ่มต้นค่า ตัวอย่างโค้ดต่อไปนี้แสดงให้เห็น
package main
import "fmt"
func main() {
var fn func(int) int
fmt.Println(fn == nil) // true
}
การเรียกใช้ฟังก์ชัน nil
จะส่งผลให้เกิด panic
nil ใน Interfaces
Interface เป็นคุณสมบัติที่สำคัญใน Go แทนชนิดข้อมูลนามธรรม เมื่อประกาศตัวแปร interface ใหม่โดยไม่ให้การใช้งานที่เป็นรูปธรรม ค่าของมันจะเป็น nil
ตัวอย่างเช่น
package main
import "fmt"
func main() {
var i interface{}
fmt.Println(i == nil) // true
}
ภายใน Go ตัวแปรชนิด interface{}
ประกอบด้วยสองส่วน: ชนิด (Type) และค่า (Value) ตัวแปร interface{} จะถือว่า nil เฉพาะเมื่อไม่มีทั้งชนิดและค่า พิจารณาตัวอย่างต่อไปนี้:
package main
import "fmt"
type MyInterface interface {
Method()
}
type MyType struct{}
func (mt *MyType) Method() {}
func main() {
var mt *MyType = nil
var i MyInterface = mt
fmt.Println(i == nil)
}
แม้ว่า mt จะเป็นตัวชี้ nil
แต่เมื่อกำหนดให้กับชนิด interface i
แล้ว i
ยังคงเก็บข้อมูลชนิดของ MyType
ไว้ ดังนั้น i
จึงไม่ใช่ nil
แนวทางปฏิบัติที่ดีที่สุดเพื่อหลีกเลี่ยงปัญหาที่เกี่ยวข้องกับ nil
- ก่อนที่จะใช้ตัวแปรประเภทตัวชี้, slice, map, channel และฟังก์ชัน ให้ตรวจสอบว่าเป็น nil หรือไม่
- ทำความเข้าใจความแตกต่างระหว่างค่าศูนย์และ nil สำหรับชนิดข้อมูลบางชนิด เช่น slice, map, channel และ interface, nil แทนค่าศูนย์ของพวกมัน อย่างไรก็ตาม ค่าศูนย์สำหรับชนิดข้อมูลไม่จำเป็นต้องเป็น nil (เช่น ชนิดข้อมูลตัวเลขและ struct)
- เมื่อฟังก์ชันส่งคืนชนิด interface ให้หลีกเลี่ยงการส่งคืนตัวชี้ nil ของชนิดข้อมูลที่เป็นรูปธรรม เนื่องจากอาจทำให้เกิดความสับสนเมื่อค่า interface ไม่ใช่ nil
- เมื่อฟังก์ชันส่งคืนข้อผิดพลาด ให้ส่งคืน
nil
แทนที่จะเป็น instance nil ของชนิดข้อผิดพลาดหากไม่มีข้อผิดพลาดเกิดขึ้น - ก่อนที่จะปิดทรัพยากร เช่น ไฟล์และการเชื่อมต่อฐานข้อมูล ให้ตรวจสอบว่าเป็น nil หรือไม่เพื่อหลีกเลี่ยงการอ้างอิงตัวชี้ nil
สรุป
nil
เป็นแนวคิดที่สำคัญใน Golang และการทำความเข้าใจอย่างลึกซึ้งเกี่ยวกับการประยุกต์ใช้ในโปรแกรม Go นั้นมีความสำคัญอย่างยิ่งต่อการเขียนโค้ด Go คุณภาพสูง บทความนี้มีจุดมุ่งหมายเพื่อช่วยให้คุณเข้าใจความรู้เกี่ยวกับ nil ได้ดียิ่งขึ้น