struct
là kiểu người dùng định nghĩa, chứa tập hợp các thuộc tính / trường được đặt tên. Nó được sử dụng để nhóm các dữ liệu được liên kết lại với nhau để tạo thành một đơn vị đơn. Bất kỳ một thực thể nào có một tập hợp các thuộc tính đều có thể được biểu diễn bằng struct
. Nếu bạn hiểu biết về hướng đối tượng, bạn có thể coi struct
như một class đơn giản có hỗ trợ các thành phần nhưng không có kế thừa.
Định nghĩa kiểu struct
Bạn có thể định nghĩa một kiểu struct
mới như sau
type Person struct { FirstName string LastName string Age int }
Từ khóa type
dùng để khai báo một kiểu mới. Tiếp theo sau nó là tên của kiểu – Person
và từ khóa struct
để biểu thị rằng chúng ta đang định nghĩa một struct
. struct chứa danh sách các trường bên trong dấu ngoặc nhọn. Mỗi trường bao gồm tên và kiểu.
Bạn có thể thu gọn các trường cùng kiểu như sau:
type Person struct { FirstName, LastName string Age int }
Khai báo và khởi tạo struct
Khai báo biến kiểu struct
Giống như các kiểu dữ liệu khác, bạn có thể khai báo biến của kiểu struct
như sau
// Khai báo biến kiểu 'Person' var p Person // Tất cả các trường struct được khởi tạo với giá trị bằng 0 hoặc trống
Đoạn code bên trên tạo một biến kiểu Person
với giá trị mặc định là 0 hoặc trống. Đối với một struct, các trường dựa theo kiểu của nó sẽ được thiết lập giá trị mặc định tương ứng. Nên các trường fistName
và lastName
được thiết lập bằng ""
, và Age được thiết lập bằng 0.
Khởi tạo struct
Bạn có thể khởi tạo một biến của kiểu struct
sử dụng chữ struct như sau
// Khởi tạo một struct thêm vào giá trị của tất cả các trường. var p = Person{"Rajeev", "Singh", 26}
Bạn cần thêm vào giá trị các trường theo đúng thứ tự mà chúng được khai báo trong struct
. Do đó bạn không thể chỉ khởi tạo một trường con cúa struct bên trên
var p = Person{"Rajeev"} // Compiler Error: too few values in struct initializer
Đặt tên các trường khi khởi tạo struct
Go cũng hỗ trợ cú pháp tên: giá trị
để khởi tạo một struct (thứ tự các trường sẽ không bắt buộc khi sử dụng cú pháp này)
var p = Person{FirstName: "Rajeev", LastName: "Singh", Age: 25}
Bạn có thể chia tách các trường bằng các xuống dòng để dễ đọc hơn (dấu phẩy là bắt buộc trong trường hợp này)
var p = Person{ FirstName: "John", LastName: "Snow", Age: 45, }
Cú pháp tên: giá trị
cho phép chúng ta có thể chỉ khai báo một trường con trong các trường của struct. Tất cả các trường không được khởi tạo sẽ được thiết lập giá trị 0 hoặc “” tương ứng với kiểu trường
var p = Person{FirstName: "Alien"} // LastName: "", Age: 0
var p = Person{} // FirstName: "", LastName: "", Age: 0
Sau đây là toàn bộ về ví dụ khai báo và khởi tạo một struct
package main import ( "fmt" ) // Định nghĩa kiểu struct type Person struct { FirstName string LastName string Age int } func main() { // Khai báo biến của kiểu `struct` var p Person // // tất cả các trường được khởi tạo với giá trị 0 fmt.Println(p) // khai báo và khởi tạo struct sử dụng chữ struct p1 := Person{"Rajeev", "Singh", 26} fmt.Println("Person1: ", p1) // tên các trường khi khởi tạo struct p2 := Person{ FirstName: "John", LastName: "Snow", Age: 45, } fmt.Println("Person2: ", p2) // các trường không khởi tạo được thiết lập mặc định 0 hoặc "" p3 := Person{FirstName: "Robert"} fmt.Println("Person3: ", p3) }
Kết quả
{ 0} Person1: {Rajeev Singh 26} Person2: {John Snow 45} Person3: {Robert 0}
Truy cập các trường của struct
Các bạn có thể truy cập từng trường của struct sử dụng toán tử dấu chấm (.)
package main import ( "fmt" ) type Car struct { Name, Model, Color string WeightInKg float64 } func main() { c := Car{ Name: "Ferrari", Model: "GTC4", Color: "Red", WeightInKg: 1920, } // Truy cập các trường struct fields sử dụng toán tử . fmt.Println("Car Name: ", c.Name) fmt.Println("Car Color: ", c.Color) // gán giá trị mới cho một trường của struct c.Color = "Black" fmt.Println("Car: ", c) }
Kết quả
Car Name: Ferrari Car Color: Red Car: {Ferrari GTC4 Black 1920}
Con trỏ đến một struct
Bạn có thể có được con trỏ đến một struct bằng cách sử dụng toán tử &
package main import ( "fmt" ) type Student struct { RollNumber int Name string } func main() { // instance của kiểu struct student s := Student{11, "Jack"} // con trỏ đến struct student ps := &s fmt.Println(ps) // Truy cập các trường struct bằng con trỏ fmt.Println((*ps).Name) fmt.Println(ps.Name) // Giống như trên: không cần truy cập rõ ràng vào vùng nhớ ps.RollNumber = 31 fmt.Println(ps) }
Kết quả
&{11 Jack} Jack Jack &{31 Jack}
Go cho phép chúng ta truy cập trực tiếp vào các trường của struct thông qua con trỏ mà không cần truy cập rõ ràng vào vùng nhớ.
Tạo một struct và con trỏ sử dụng hàm new() có sẵn
Bạn cũng có thể sử dụng hàm new()
có sẵn để tạo một instance của struct. Hàm new() cấp phát bộ nhớ cần thiết cho các trường của struct, khởi tạo giá trị các trường bằng 0 và trả về một con trỏ cho struct được cấp phát mới.
package main import "fmt" type Employee struct { Id int Name string } func main() { // Bạn có thể có một con trỏ đến struct sử dụng hàm new() có sẵn // Cấp phát đủ bộ nhớ cho giá trị truyền vào kiểu struct, và trả về một con trỏ đến nó pEmp := new(Employee) pEmp.Id = 1000 pEmp.Name = "Sachin" fmt.Println(pEmp) }
Kết quả
&{1000 Sachin}
Export hoặc không Export struct và các trường của struct
Bất kỳ kiểu struct nào mà bắt đầu bằng ký tự viết hoa là được Export và có thể truy cập bên ngoài các package. Tương tự như vậy, bất kỳ trường nào của struct bắt đầu bằng ký tự viết hoa là được Export.
Ngược lại, tên được bắt đầu bằng ký tự viết thường chỉ có thể truy cập bên trong của cùng package.
Hãy cùng xem ví dụ sau, hay xem qua về cấu trúc phân cấp các package của một chương trình Go
$GOPATH/src example main main.go model address.go customer.go
customer.go
package model type Customer struct { // kiểu struct được export Id int // trường được export Name string // trường được export addr address // trường không được export (chỉ truy cập bên trong package `model`) married bool // trường không được export (chỉ truy cập bên trong package `model`) }
address.go
package model // struct không được (chỉ truy cập bên trong package `model`) type address struct { houseNo, street, city, state, country string zipCode int }
main.go
package main import ( "fmt" "example/model" ) func main() { c := model.Customer{ Id: 1, Name: "Rajeev Singh", } c.married = true // Lỗi: can not refer to unexported field or method a := model.address{} // Lỗi: can not refer to unexported name fmt.Println("Programmer = ", c); }
Bạn có thể thấy các tên address
và married
là không được Export và không thể truy cập tử package main
struct là kiểu giá trị
struct là kiểu giá trị. Khi bạn gán một biến struct cho một biến khác, một bản sao mới của struct được tạo và được gán. Tương tự như vậy, khi bạn truyền một struct cho một hàm, hàm sẽ nhận một bản sao của struct
import "fmt" type Point struct { X float64 Y float64 } func main() { // Structs kiểu giá trị. p1 := Point{10, 20} p2 := p1 // bản sao struct `p1` được gán cho `p2` fmt.Println("p1 = ", p1) fmt.Println("p2 = ", p2) p2.X = 15 fmt.Println("\nAfter modifying p2:") fmt.Println("p1 = ", p1) fmt.Println("p2 = ", p2) }
Kết quả
p1 = {10 20} p2 = {10 20} After modifying p2: p1 = {10 20} p2 = {15 20}
So sánh bằng struct
Hai biến struct được cho là bằng nhau nếu tất cả các trường tương ứng là bằng nhau
package main import "fmt" type Point struct { X float64 Y float64 } func main() { // Hai struct bằng nhau nếu tất cả các trường tương ứng là bằng nhau. p1 := Point{3.4, 5.2} p2 := Point{3.4, 5.2} if p1 == p2 { fmt.Println("Point p1 and p2 are equal.") } else { fmt.Println("Point p1 and p2 are not equal.") } }
Kết quả
Point p1 and p2 are equal.