String ใน Go ทำงานยังไง ?

วันนี้เรามาดูกันว่า string ใน Go ทำงานยังไง แล้วส่งผ่าน function มันจะ copy ค่ารึเปล่า ?

“white printer paper lot” by Annie Spratt on Unsplash

ลองดูตัวอย่างสั้น ๆ กัน

// Go
func sayHello(name string) {
fmt.Printf("Hello, %s\n", name)
}
func main() {
myName := "acoshift"
sayHello(myName)
}

คิดว่า data acoshift จะถูก copy ไปใน function sayHello ไหม ?


คำตอบคือ string ใน Go ทำงานเหมือน slice ตัวนึงนี่แหละ เป็น struct ที่เก็บ pointer 1 ตัวที่ชี้ไปที่ memory ที่เก็บข้อความ กับความยาวของ string

// C
typedef struct {
char *str;
int len;
} string;

เวลาที่เรา assign ค่าให้กับตัวแปรที่เป็น string

// Go
myName := "acoshift"

ก็เหมือนกับเราใส่ค่าลงไปใน struct

// C
string myName;
myName.len = 8;
myName.str = malloc(sizeof(char) * myName.len);
memcpy(myName.str, "acoshift", sizeof(char) * myName.len);

เวลาเราส่งตัวแปร string เข้าไปใน function สิ่งที่ Go จะทำก็คือ copy struct แต่จะไม่ copy data ใน str

แสดงว่าไม่ว่า string เราจะยาวแค่ไหน หรือสั้นแค่ไหน เวลาส่งเข้าไปใน function จะใช้ memory เท่ากันเสมอ

ดังนั้น

// Go
func sayHello(name string) {
fmt.Printf("Hello, %s\n", name)
}

จึงหมายถึง

// C
void sayHello(string name) {
printf("Hello, %.*s\n", name.len, name.str);
}

แล้วเวลาแก้ค่า string หรือเอามาต่อกันหล่ะ ?

Go จะไม่แก้ค่าที่ตัวแปร str ชี้อยู่ตรง ๆ แต่จะสร้างใหม่ แล้วให้ str ไปชี้ตรงที่ใหม่แทน

จึงเป็นที่มาว่า ทำไมเราเอา string มาต่อกันหลาย ๆ รอบแล้วช้าว่าการใช้พวก string builder เพราะมันต้อง copy ไปทุกครั้งนั้นเอง

// Go
func appendString(s1, s2 string) string {
return s1 + s2
}

จะทำงานแบบนี้

// C
string appendString(string s1, string s2) {
string r;
r.len = s1.len + s2.len;
r.str = malloc(sizeof(char) * r.len);
memcpy(r.str, s1.str, sizeof(char) * s1.len);
memcpy(r.str + sizeof(char) * s1.len, s2.str, sizeof(char) * s2.len);
return r;
}

ลองดู function ที่แก้ค่า string

// Go
func mutateString1(s string) {
s = "John Titor"
}

คือการที่เรา pass by value ของ ตัว struct string ให้กับตัวแปร s ทำให้ struct ตัวที่ก่อน copy เข้ามาไม่มีทางที่จะสามารถเปลี่ยนได้

// C
void mutateString1(string s) {
s.len = 10;
s.str = malloc(sizeof(char) * s.len);
memcpy(s.str, "John Titor", sizeof(char) * s.len);
}

แสดงว่าถ้าเราต้องการแก้ค่าตัวแปรใน string เราก็ต้องส่ง struct ของ string เข้าไปเป็น pointer ก็จะสามารถแก้ค่าได้

// Go
func mutateString2(s *string) {
*s = "John Titor"
}

หมายถึง

// C
void mutateString2(string *s) {
s->len = 10;
s->str = malloc(sizeof(char) * s->len);
memcpy(s->str, "John Titor", sizeof(char) * s->len);
}

ลองมาดู Code เต็ม ๆ กันเลย~~~!!!