Go is language that use copy value semantic (no reference semantic like C++ or Rust).
num := 10 // copy ค่า 10 ลงหน่วยความจำที่ชื่อไว้ว่า num
num = 20 // copy ค่า 20 ลงหน่วยความจำที่ชื่อไว้ว่า num
num2 := num // copy ค่าจาก num ลงหน่วยความจำที่ชื่อว่า num2
num = 30
fmt.Println(num2) // ยังคงเป็น 20 เพราะเป็นตัวแปรคนละตัว
func main() {
num := 20 // ตั้งแต่ประกาศตัวแปร
fmt.Println(num) // ใช้ใน
} // จนสิ้นสุด การทำงานของ ฟังก์ชัน
func sayHello(nameParameter string) { // parameter variable เป็นตัวแปรใน scope ของ sayHello function
fmt.Println("Hello, ", nameParameter)
}
func main() {
name := "Por" // ตัวนี้เป็นตัวแปรใน scope ของ main function
sayHello(name) // ทำงานเหมือนกับ nameParameter = name คือ copy ค่าจาก name ใน scope ของ main ให้กับ nameParameter ใน scope ของ sayHello
}
func setNamePrefix(name string) {
name = "Mr. " + name
}
func main() {
name := "Por"
setNamePrefix(name)
fmt.Println(name) // Por , ไม่เปลี่ยน เพราะเกิดการ copy ค่าไปแล้ว ตัวแปร name คนละ scope กัน ก็คือตัวแปรคนละตัวนั่นละ เปลี่ยนอีกตัวใน setNamePrefix ไม่กระทบกับอีกตัวที่อยู่ใน main
}
No the direct way to share variable in Go, but it has the indirect way to access same memory location through pointer type
num := 10
ptrNum := &num // ใช้ operator `&` เพื่อเอา memory location (address) กำหนดในตัวแปร ptrNum ซึ่งจะมี type เป็น *int (pointer ของ int)
// แบบนี้ไม่ได้นะ ptrNum ไม่ใช่ตัวแปรตัวเลข แต่เป็น address ของ int
ptrNum = 20
// ต้องใช้ แบบนี้ เรียกว่า dereference (`*` คือ dereference operator) เพื่อกำหนดค่าใน memory location ที่อยู่ใน ptrNum
*ptrNum = 20
fmt.Println(num) // 20, เปลี่ยนเป็น 20 ด้วยเพราะใช้ deference operator ทำให้เปลี่ยนค่าใน memory location ที่เก็บอยู่ได้
func setNamePrefix(ptrName *string) {
*ptrName = "Mr. " + *ptrName
}
func main() {
name := "Por"
setNamePrefix(&name) // ใช้ operator `&` ทำให้เป็นการ copy location ของ name ซึ่งก็คือ ptrName = &name ให้กับตัวแปร ptrName
fmt.Println(name) // Mr. Por , เปลี่ยนได้แล้วเพราะในฟังก์ชัน setNamePrefix ใช้ dereference operator แก้ไขค่าใน location เดียวกัน
}
type Todo struct {
ID string
Description string
}
func createTodo(id string, description string) Todo {
todo := Todo { // ตัวแปร todo อยู่ใน scope ของ createTodo
ID: id,
Description: description,
}
return todo // จุดนี้คือการ copy ค่าของ todo ออกไป เพราะจะเอา todo ออกไปเลยไม่ได้เนื่องจากมันจะหายไปหลังจากจบ scope
} // สิ้นสุด scope ตรงนี้
func main() {
tado := createTodo("1", "ซักผ้า") // tado คือของที่ copy มาจากตอน return todo
}
แต่ถ้าเราไม่ต้องการ copy ค่าล่ะ อยากได้ memory location เดิมที่สร้างไว้กับ ตัวแปร todo ใน createTodo เอาไว้แล้ว เราทำตรงๆไม่ได้ แต่ก็ทำได้อ้อมๆโดยใช้ pointer ช่วย
type Todo struct {
ID string
Description string
}
func createTodo(id string, description string) Todo {
todo := Todo { // ตัวแปร todo อยู่ใน scope ของ createTodo
ID: id,
Description: description,
}
return &todo // จุดนี้คือการ copy ค่าของ memory location ของ todo ไปแทน (ซึ่งก็คือตัวแปร pointer ของ todo `type *Todo` นั่นเอง)
} // สิ้นสุด scope ตรงนี้ แต่ว่า memory location เดียวกันยังอ้างอิงได้จาก pointer ของ todo ที่ return กลับไป
func main() {
tado := createTodo("1", "ซักผ้า") // tado คือ pointer ของ todo แล้ว (type *Todo) ไม่ใช่ todo โดยตรง แต่อ้างอิงถึง memory location เดิมที่ถูกสร้างใน createTodo นั่นแหละได้ผ่าน dereference operator
fmt.Println(*todo) // {1, ซักผ้า}
}
type Todo struct {
ID string
Description string
}
func main() {
num := 10
ptrNum := &num
// ต้องใช้ dereference operator เมื่อเปลี่ยนค่าของ memory ที่ ptrNum ชี้อยู่
*prtNum = 20
fmt.Println(num) // 20
todo := Todo{"1", "ซักผ้่า"}
ptrTodo := &todo
// ไม่ต้องทำแบบนี้ก็ได้
id := (*ptrNum).ID
desc := (*ptrNum).Description
// ใช้แบบนี้พอ เหมือน type Todo ปกติ
id := todo.ID
desc := todo.Description
fmt.Println(id, description) // 1 ซักผ้า
id := ptrTodo.ID
desc := ptrTodo.Description
fmt.Println(id, description) // 1 ซักผ้า
ptrTodo.ID = 2
fmt.Println(todo) // {2, ซักผ้า} , todo เปลี่ยนด้วย เพราะแชร์ memory location กันอยู่
// หรือถ้าจะเปลี่ยนทั้งก้อนเลยไม่ใช่แค่บาง field ยังต้องใช้ dereference operator อยู่เช่น
*ptrTodo = Todo{"2", "ล้างจาน"}
fmt.Println(todo) // {2 ล้างจาน}, todo เปลี่ยนด้วย
// แต่แบบนี้ไม่ได้นะ มันคือเปลี่ยน memory location ให้ ptrTodo แชร์กับตัวอื่นแทน ไม่ใช่เปลี่ยนค่าที่แชร์อยู่เดิม
todo := Todo{"1", "ซักผ้่า"}
ptrTodo := &todo
ptrTodo = &Todo{"2", "ล้างจาน"} // เปลี่ยนไปแชร์ memory กับ location อื่นแล้ว
fmt.Println(todo) // {1, ซักผ้า} todo ยังเป็นค่าเดิม
}
- When we design a function to read/update partial of complex data structure.
type Config struct {
// it has many fields
}
func ProcessingData(c *Config) {
if c.Flag {
}
}
type Stat struct {
}
func Count(s *Stat) {
s.Counter++
}
- When we design a function to create new complex data structure.
type Config struct {
// it has many fields
}
func LoadConfig() *Config {
var conf Config
// has many logic to create Config
return &conf
}
- Method receiver is just an another parameter.
type Config struct {
// has many fields
}
func (c *Config) Flag() bool {
return c.flag
}
func main() {
conf := LoadConfig()
if conf.Flag() { // call method Flag by passing &conf to (c *Config) automatically
}
}