Skip to content

Cú pháp cơ bản

Cú pháp cơ bản của Go rất đơn giản và dễ hiểu, hãy bắt đầu với một ví dụ đơn giản nhất.

go
package main

import "fmt"

func main() {
   fmt.Println("Xin chào thế giới!")
}
  • Từ khóa package khai báo tệp go hiện tại thuộc về gói nào, tệp入口 phải được khai báo là gói main, hàm入口 là hàm main, khi tùy chỉnh gói và hàm, nên tránh trùng tên với chúng.

  • import là từ khóa để nhập, theo sau là tên gói được nhập.

  • func là từ khóa khai báo hàm, dùng để khai báo một hàm.

  • fmt.Println("Xin chào thế giới!") là một câu lệnh, gọi hàm Println trong gói fmt để xuất.

Trên đây là một giới thiệu cú pháp đơn giản, sau đây sẽ tìm hiểu chi tiết hơn về các khái niệm bên trong.

Gói

Trong Go, chương trình được xây dựng bằng cách liên kết các gói với nhau. Đơn vị cơ bản để nhập trong Go là một gói, chứ không phải tệp .go. Gói thực chất là một thư mục, tên tiếng Anh là package, gói chia sẻ tất cả các biến, hằng số và tất cả các loại được định nghĩa. Phong cách đặt tên gói nên là chữ thường và nên ngắn gọn.

Khả năng hiển thị

Như đã đề cập trước đó, gói chia sẻ tất cả các biến, hằng số và tất cả các loại được định nghĩa, nhưng đối với bên ngoài gói thì không phải vậy, đôi khi bạn không muốn người khác truy cập một loại nào đó, vì vậy cần kiểm soát khả năng hiển thị. Bạn có thể đã thấy các từ khóa như Public, Private trong các ngôn ngữ OOP khác, nhưng trong Go không có những từ khóa này, cách kiểm soát khả năng hiển thị của nó rất đơn giản, quy tắc như sau

  • Tên bắt đầu bằng chữ cái viết hoa, tức là loại/biến/hằng số công khai
  • Tên bắt đầu bằng chữ cái viết thường hoặc dấu gạch dưới, tức là loại/biến/hằng số riêng tư

Ví dụ như ví dụ dưới đây, hằng số MyName là công khai, còn hằng số mySalary là riêng tư.

go
package example

// Công khai
const MyName = "jack"

// Riêng tư
const mySalary = 20_000

Quy tắc khả năng hiển thị này áp dụng cho mọi nơi trong toàn bộ ngôn ngữ Go.

Nhập

Nhập một gói để sử dụng các loại/phương thức/hàm/biến trong gói đó, cú pháp nhập là import cộng với tên gói

go
package main

import "example"

Khi nhập nhiều gói, bạn có thể viết như sau

go
package main

import "example"
import "example1"

Hoặc có thể dùng ngoặc đơn括起来, cách viết này thực tế được sử dụng phổ biến hơn.

go
package main

import (
  "example"
  "example1"
)

Nếu có tên gói trùng lặp hoặc tên gói phức tạp, bạn cũng có thể đặt bí danh cho chúng

go
package main

import (
  e "example"
  e1 "example1"
)

Khi bí danh là dấu gạch dưới _ thì đó là nhập ẩn danh, gói được nhập ẩn danh không thể được sử dụng, việc này thường được thực hiện để tải hàm init trong gói, nhưng không cần sử dụng các loại trong gói, một ví dụ phổ biến là đăng ký driver cơ sở dữ liệu, nhưng bạn không cần phải sử dụng driver thủ công.

go
package main

import (
  e "example"
  _ "mysql-driver-go"
)

Sau khi nhập, khi muốn truy cập các loại trong gói, bạn có thể truy cập thông qua tên_gói.định_danh, như trong ví dụ dưới đây, nếu bạn cố truy cập một loại riêng tư, trình biên dịch sẽ cho bạn biết không thể truy cập.

go
package main

import (
  "example"
   "fmt"
)

func main() {
    fmt.Println(example.MyName)
}

Có một cách nhập đặc biệt là nhập tất cả các loại trong gói vào phạm vi gói hiện tại, với cách nhập này, các loại không cần toán tử . để truy cập nữa, nhưng nếu có loại trùng tên sẽ không thể biên dịch được.

go
package main

import (
  . "example"
)

WARNING

Trong Go không thể nhập vòng lặp, dù là trực tiếp hay gián tiếp. Ví dụ, gói A nhập gói B, gói B cũng nhập gói A, đây là nhập vòng lặp trực tiếp, gói A nhập gói C, gói C nhập gói B, gói B lại nhập gói A, đây là nhập vòng lặp gián tiếp, nếu có nhập vòng lặp sẽ không thể biên dịch được.

Gói nội bộ

Trong Go, quy ước rằng một gói có tên là internal trong một gói là gói nội bộ, gói bên ngoài sẽ không thể truy cập bất kỳ nội dung nào trong gói nội bộ, nếu không thì biên dịch sẽ không thành công, xem một ví dụ dưới đây.

/home/user/go/
    src/
        crash/
            bang/              (mã go trong package bang)
                b.go
        foo/                   (mã go trong package foo)
            f.go
            bar/               (mã go trong package bar)
                x.go
            internal/
                baz/           (mã go trong package baz)
                    z.go
            quux/              (mã go trong package main)
                y.go

Từ cấu trúc tệp có thể thấy, gói crash không thể truy cập các loại trong gói baz.

Chú thích

Go hỗ trợ chú thích một dòng và chú thích nhiều dòng, nên cách một khoảng trắng giữa chú thích và nội dung, ví dụ

go
// Đây là gói main
package main

// Đã nhập gói fmt
import "fmt"

/*
*
Đây là hàm khởi động hàm main
*/
func main() {
  // Đây là một câu lệnh
  fmt.Println("Xin chào thế giới!")
}

Định danh

Định danh là một tên, dùng để đặt tên gói, đặt tên hàm, đặt tên biến, v.v., quy tắc đặt tên như sau

  • Chỉ có thể bao gồm chữ cái, số, dấu gạch dưới
  • Chỉ có thể bắt đầu bằng chữ cái và dấu gạch dưới
  • Phân biệt chữ hoa chữ thường nghiêm ngặt
  • Không được trùng với bất kỳ định danh nào đã tồn tại, tức là duy nhất trong gói
  • Không được xung đột với bất kỳ từ khóa tích hợp nào của Go

Dưới đây liệt kê tất cả các từ khóa tích hợp, bạn cũng có thể đến Sổ tay tham khảo - Định danh để xem thêm chi tiết

go
break        default      func         interface    select
case         defer        go           map          struct
chan         else         goto         package      switch
const        fallthrough  if           range        type
continue     for          import       return       var

Toán tử

Dưới đây là thứ tự ưu tiên của các toán tử được hỗ trợ trong ngôn ngữ Go, bạn cũng có thể đến Sổ tay tham khảo - Toán tử để xem thêm chi tiết.

Precedence    Operator
    5             *  /  %  <<  >>  &  &^
    4             +  -  |  ^
    3             ==  !=  <  <=  >  >=
    2             &&
    1             ||

Có một điểm cần lưu ý, trong ngôn ngữ Go không chọn ~ làm toán tử phủ định, mà tái sử dụng ký hiệu ^, khi hai số sử dụng ^, ví dụ a^b, nó là toán tử XOR, chỉ khi sử dụng với một số, ví dụ ^a, thì nó là toán tử phủ định. Go cũng hỗ trợ toán tử gán tăng cường, như sau.

go
a += 1
a /= 2
a &^= 2

TIP

Ngôn ngữ Go không có toán tử tự tăng và tự giảm, chúng được hạ cấp thành câu lệnh statement, và quy định chỉ có thể đứng sau toán hạng, vì vậy không cần phải băn khoăn về vấn đề i++++i.

a++ // Đúng
++a // Sai
a-- // Đúng

Một điểm nữa là chúng không còn có giá trị trả về, vì vậy các câu lệnh như a = b++ là sai.

Giá trị chữ

Giá trị chữ, theo thuật ngữ khoa học máy tính là ký hiệu dùng để biểu thị một giá trị cố định trong mã nguồn, còn gọi là giá trị chữ. Hai cách gọi này đều có cùng ý nghĩa, viết cái gì thì giá trị là cái đó, giá trị là giá trị "theo nghĩa đen".

Giá trị chữ số nguyên

Để dễ đọc, cho phép sử dụng dấu gạch dưới _ để phân chia số, nhưng chỉ được phép sử dụng sau ký hiệu tiền tốgiữa các chữ số.

go
24 // 24
024 // 24
2_4 // 24
0_2_4 // 24
10_000 // 10k
100_000 // 100k
0O24 // 20
0b00 // 0
0x00 // 0
0x0_0 // 0

Giá trị chữ số dấu phẩy động

Thông qua các tiền tố khác nhau có thể biểu thị số dấu phẩy động của các hệ cơ số khác nhau

go
0.
72.40
072.40       // == 72.40
2.71828
1.e+0
6.67428e-11
1E6
.25
.12345E+5
1_5.         // == 15.0
0.15e+0_2    // == 15.0

0x1p-2       // == 0.25
0x2.p10      // == 2048.0
0x1.Fp+0     // == 1.9375
0X.8p-0      // == 0.5
0X_1FFFP-16  // == 0.1249847412109375
0x15e-2      // == 0x15e - 2 (phép trừ số nguyên)

Giá trị chữ số phức

go
0i
0123i         // == 123i
0o123i        // == 0o123 * 1i == 83i
0xabci        // == 0xabc * 1i == 2748i
0.i
2.71828i
1.e+0i
6.67428e-11i
1E6i
.25i
.12345E+5i
0x1p-2i       // == 0x1p-2 * 1i == 0.25i

Giá trị chữ ký tự

Giá trị chữ ký tự phải được括 trong dấu nháy đơn '', ký tự trong Go hoàn toàn tương thích với utf8.

go
'a'
'ä'
''
'\t'
'\000'
'\007'
'\377'
'\x07'
'\xff'
'\u12e4'
'\U00101234'

Ký tự thoát

Các ký tự thoát có sẵn trong Go

\a   U+0007 Ký hiệu chuông
\b   U+0008 Ký hiệu lùi
\f   U+000C Ký hiệu chuyển trang
\n   U+000A Ký hiệu xuống dòng
\r   U+000D Ký hiệu quay lại
\t   U+0009 Ký hiệu tab ngang
\v   U+000B Ký hiệu tab dọc
\\   U+005C Ký hiệu thoát dấu gạch chéo ngược
\'   U+0027 Ký hiệu thoát dấu nháy đơn (chỉ có hiệu lực trong ký tự)
\"   U+0022 Ký hiệu thoát dấu nháy kép (chỉ có hiệu lực trong chuỗi)

Giá trị chữ chuỗi

Giá trị chữ chuỗi phải được括 trong dấu nháy kép "" hoặc dấu nháy ngược `` (chuỗi dấu nháy ngược không cho phép thoát)

go
`abc`                // "abc"
`\n
\n`                  // "\\n\n\\n"
"\n"
"\""                 // `"`
"Hello, world!\n"
"Hôm nay thời tiết đẹp"
"日本語"
"\u65e5\U00008a9e"
"\xff\u00FF"

Hàm

Cách khai báo hàm trong Go được thực hiện thông qua từ khóa func, tương tự như hầu hết các ngôn ngữ

go
func main() {
  println(1)
}

Tuy nhiên, có hai điểm khác biệt trong hàm của Go, điểm đầu tiên là kiểu tham số đứng sau, như dưới đây

func Hello(name string) {
  fmt.Println(name)
}

Điểm khác biệt thứ hai là nhiều giá trị trả về, và có thể có tên

go
func Pos() () (x, y float64) {
    ...
}

Phong cách

Về phần phong cách mã hóa, Go bắt buộc tất cả mọi người thống nhất cùng một phong cách, Go chính thức cung cấp một công cụ định dạng gofmt, có thể sử dụng thông qua dòng lệnh, công cụ định dạng này không có bất kỳ tham số định dạng nào để truyền, chỉ có hai tham số duy nhất cũng chỉ là xuất quá trình định dạng, vì vậy hoàn toàn không hỗ trợ tùy chỉnh, nghĩa là tất cả mã sau khi định dạng bằng công cụ này đều có cùng một phong cách mã, điều này sẽ giảm đáng kể gánh nặng tinh thần cho người bảo trì, vì vậy việc theo đuổi cá tính trong phần này rõ ràng không phải là lựa chọn khôn ngoan.

Dưới đây sẽ liệt kê đơn giản một số quy tắc,平时 khi viết mã cũng có thể lưu ý một chút.

Dấu ngoặc nhọn hàm xuống dòng

Về việc dấu ngoặc nhọn sau hàm có nên xuống dòng hay không, hầu như mỗi lập trình viên đều có thể đưa ra lý do của riêng mình, trong Go tất cả các dấu ngoặc nhọn không nên xuống dòng

go
// Ví dụ đúng
func main() {
  fmt.Println("Xin chào thế giới!")
}

Nếu bạn thực sự làm như vậy, như dưới đây

go
// Ví dụ sai
func main()
{
  fmt.Println("Xin chào thế giới!")
}

Mã như vậy thậm chí không thể biên dịch được, vì vậy Go bắt buộc tất cả các lập trình viên không xuống dòng dấu ngoặc sau hàm.

Thụt lề mã

Go mặc định sử dụng Tab tức là ký tự tab để thụt lề, chỉ trong một số trường hợp đặc biệt sẽ sử dụng dấu cách.

Khoảng cách mã

Hầu hết các khoảng cách trong Go đều có ý nghĩa, ở một mức độ nào đó, điều này cũng đại diện cho cách trình biên dịch xem mã của bạn, ví dụ như phép toán toán học dưới đây

2*9 + 1/3*2

Như chúng ta đã biết, ưu tiên của phép nhân cao hơn phép cộng, sau khi định dạng, khoảng cách giữa các ký hiệu * sẽ chặt chẽ hơn, có nghĩa là ưu tiên thực hiện phép tính, còn khoảng cách gần ký hiệu + thì lớn hơn, đại diện cho việc thực hiện sau.

Bỏ qua dấu ngoặc nhọn

Trong các ngôn ngữ khác, câu lệnh if và for thường có thể được viết tắt, như dưới đây

c
for (int i=0; i < 10; i++) printf("%d", i)

Nhưng trong Go thì không, bạn có thể chỉ viết một dòng, nhưng phải thêm dấu ngoặc nhọn

go
for i := 0; i < 10; i++ {fmt.Println(i)}

Biểu thức tam nguyên

Go không có biểu thức tam nguyên, vì vậy mã như dưới đây không thể biên dịch được

go
var c = a > b ? a : b

Thông qua bài viết này, bạn có thể có nhận thức sơ bộ về cú pháp Go, các nội dung tiếp theo sẽ được triển khai chi tiết hơn.

Golang by www.golangdev.cn edit