Go – Structure

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 fistNamelastName đượ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 addressmarried 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.

 

You May Also Like

About the Author: Nguyen Dinh Thuc

Leave a Reply

Your email address will not be published.