DEV Community

Gophernment
Gophernment

Posted on

Null, nil, None — คำว่า 'ไม่มีค่า' ในแต่ละภาษา

Null, nil, None — คำว่า "ไม่มีค่า" ในแต่ละภาษา

เวลาเราเขียนโปรแกรม — วันนึงเราจะต้องเจอ "การไม่มีค่า"

บางภาษามันคือ null บางภาษามันคือ nil บางภาษามันคือ None — แต่มันคือสิ่งเดียวกันใช่ไหม?

ไม่ใช่ — และความต่างเล็ก ๆ นี่แหละที่ทำให้โปรแกรมพังได้


ภาพรวม — แต่ละภาษาเรียกชื่ออะไร

ภาษา ชื่อเรียก ตัวอย่าง
Java null String s = null;
JavaScript null + undefined let x = null; let y;
Python None x = None
Ruby nil x = nil
C/C++ NULL / nullptr int* p = NULL;
Go nil + zero values var s *string = nil

เห็นว่า Ruby กับ Go ใช้ nil เหมือนกัน — แต่เบื้องหลังต่างกันโดยสิ้นเชิง


Java — null คือตัวปัญหา

String name = null;
System.out.println(name.length());  // 💥 NullPointerException!
Enter fullscreen mode Exit fullscreen mode

ใน Java — null คือ "การไม่มี object" — และทุกครั้งที่คุณเรียก method บน null — โปรแกรมพัง

Java แก้ด้วยการเช็ค if (x != null) ทุกที่ — หรือใช้ Optional (Java 8+) — แต่ null ก็ยังแฝงตัวอยู่ทุกมุม

// Java Optional — พยายามกำจัด null
Optional<String> name = Optional.ofNullable(getName());
name.ifPresent(System.out::println);
Enter fullscreen mode Exit fullscreen mode

JavaScript — มีสองแบบ ซวยสองเท่า

let x = null;        // "ฉันจงใจให้มันว่าง"
let y;               // undefined — "ฉันลืมใส่ค่า"

console.log(x);      // null
console.log(y);      // undefined
console.log(typeof null);  // "object" ← bug ในภาษา!

null == undefined;    // true  ← อ้าว?
null === undefined;   // false ← อ้าววว?
Enter fullscreen mode Exit fullscreen mode

JavaScript มีทั้ง null และ undefined — และการที่ typeof null === "object" เป็น bug ที่ฝังมาตั้งแต่ปี 1995 — แก้ไม่ได้แล้วเพราะคนใช้กันทั้งโลก


Python — None คือ singleton

x = None
y = None
print(x is y)  # True — None มีตัวเดียวในจักรวาล
Enter fullscreen mode Exit fullscreen mode

ใน Python — None เป็น singleton — มีแค่ instance เดียวในหน่วยความจำ — is ใช้เช็คได้

def get_user(id):
    return None  # "ไม่เจอ"

user = get_user(42)
if user is None:
    print("ไม่พบผู้ใช้")
Enter fullscreen mode Exit fullscreen mode

สะอาด ตรงไปตรงมา — ไม่มี NullPointerException — แต่ก็ไม่ได้ช่วยให้ปลอดภัยขึ้น — ถ้าลืมเช็ค None — โปรแกรมก็พังตอนใช้ user.name อยู่ดี


Go — nil คืออะไร? และทำไมมันถึงหลอกเรา

Go ใช้ nil — แต่ความหมายของมันเปลี่ยนไปตาม type

var p *int = nil        // nil pointer
var s []int = nil       // nil slice — แต่ใช้ append ได้!
var m map[string]int = nil  // nil map — อ่านได้ แต่เขียนไม่ได้!
var ch chan int = nil   // nil channel — block ตลอดกาล
var fn func() = nil     // nil function — เรียกแล้ว panic
Enter fullscreen mode Exit fullscreen mode

nil slice: ต่างจากภาษาอื่น — คุณใช้ append บน nil slice ได้!

var names []string       // nil slice — len=0
names = append(names, "Alice")  // ✅ ได้เลย! ไม่ต้อง make ก่อน
fmt.Println(names)       // [Alice]
Enter fullscreen mode Exit fullscreen mode

nil map: อ่านได้ แต่เขียนไม่ได้:

var m map[string]int     // nil map
_ = m["key"]             // ✅ ได้ 0 (zero value)
m["key"] = 1             // 💥 panic: assignment to entry in nil map
Enter fullscreen mode Exit fullscreen mode

หลุมพรางใน Go — nil interface ≠ nil pointer

นี่คือ bug ที่คนเขียน Go เจอแล้วเจออีก:

func getUser() *User {
    return nil  // ไม่มี user
}

func getUserAsInterface() error {
    return getUser()  // return nil pointer
}

func main() {
    err := getUserAsInterface()
    if err != nil {
        fmt.Println("มี error!")  // ❌ มันเข้าเงื่อนไขนี้ทั้งที่ควรเป็น nil!
    }
}
Enter fullscreen mode Exit fullscreen mode

ทำไม? interface ใน Go ไม่ใช่แค่ "ค่า" — มันคือ (type, value) — tuple:

var p *User = nil
fmt.Println(p == nil)  // true — pointer nil

var err error = p      // เก็บ (*User, nil) — type ไม่ใช่ nil!
fmt.Println(err == nil) // false!?
Enter fullscreen mode Exit fullscreen mode

interface err มี type = *User (ไม่ใช่ nil) — แต่ value = nil — interface โดยรวมจึง ไม่ใช่ nil

วิธีแก้: return nil แบบไม่มี type — ไม่ใช่แค่ pointer nil:

func getUserAsInterface() error {
    u := getUser()
    if u == nil {
        return nil  // ✅ nil แบบไม่มี type — interface nil จริง
    }
    return u
}
Enter fullscreen mode Exit fullscreen mode

Zero Values — Go ไม่มี "ไม่มีค่า"

แนวคิดที่ต่างจากภาษาอื่นมาก: Go ไม่มี uninitialized variable

var i int        // 0
var s string     // "" (empty string — ไม่ใช่ nil!)
var b bool       // false
var p *int       // nil (แต่ type ชัดเจนว่าเป็น *int)
Enter fullscreen mode Exit fullscreen mode

ใน Java/Python/JS — ตัวแปรที่ยังไม่ assign ค่าอาจเป็น null/None/undefined

ใน Go — ทุกอย่างมี zero value — ค่าเริ่มต้นของ type นั้น — คุณไม่ต้องกลัวว่าจะลืม initialize

type User struct {
    Name string  // "" (ไม่ใช่ nil)
    Age  int     // 0
}

var u User
fmt.Println(u.Name)  // "" — ไม่พัง!
Enter fullscreen mode Exit fullscreen mode

เปรียบเทียบกับ Java:

class User {
    String name;  // null!
    int age;      // 0
}

User u = new User();
u.name.length();  // 💥 NullPointerException!
Enter fullscreen mode Exit fullscreen mode

เทียบกัน — ภาษาไหนชนะ

null error zero value nil slice ใช้ได้ interface nil trap
Go ❌ (แต่มี interface trap) ⚠️ หลุมพราง
Java 💥 NullPointerException ❌ (Object = null)
JavaScript 💥 undefined + null
Python 💥 AttributeError
Ruby 💥 NoMethodError

สรุป

ภาษา ชื่อ จุดอ่อน
Java null NullPointerException ทุกที่ — Optional ช่วยได้นิดหน่อย
JavaScript null, undefined สองแบบ — typeof null === "object"
Python None สะอาดสุด — แต่ต้องเช็ค is None เอง
Go nil + zero values ปลอดภัยกว่า — แต่ interface nil trap หลอกได้

หัวใจของ Go: ตัวแปรทุกตัวมีค่าเสมอ — แม้จะเป็น "ค่าเริ่มต้น" — คุณจะไม่เจอ NullPointerException ใน Go — แต่คุณจะเจอ "interface nil trap" แทน

และนั่นคือบทเรียนที่คนเขียน Go ทุกคนต้องผ่าน — nil กับ nil มันไม่เหมือนกัน

📚 อ่านต่อ:

Top comments (0)