Các thành phần cơ bản
Code Golang có thể được viết với bộ mã Unicode UTF-8.
Comments
Gồm 2 loại:
- Comment theo hàng:
// hi there
. - Comment chung:
/* hi there */
.
Số nguyên
Gồm 4 loại: thập phân, nhị phân (0b, 0B
), octal (0o, 0O
) và hexa (0x, 0X
). Có thể dùng _
để ngăn cách giữa các chữ số, cho dễ đọc.
Số thực
Gồm 2 loại: số thực với chữ số thập phân (1.2e-2
), số thực với chữ số hexa (0X1p-2
)
Số ảo
Là phần ảo của số phức. Cấu tạo: 1i, 0xabci, .25i
Rune
Là một giá trị nguyên biểu thị 1 Unicode code point. Cấu tạo: 'a', '\n', 'á'
String
Gồm 2 loại: raw (ngăn cách bởi 2 dấu `) và interpreted (ngăn cách bởi 2 dấu “).
- Raw string gồm các kí tự unicode và newline.
- Interpreted string gồm các giá trị unicode và giá trị byte.
Declarations
ID trống
Được biểu thị bằng dấu _
. Mục đích: đại diện cho anonymous placeholder, đỡ xài ID thật.
Các ID đã được khai báo sẵn
Các ID sau đã được khai báo trước ở universe block.
- Kiểu:
1 2 3
bool byte complex64 complex128 error float32 float64 int int8 int16 int32 int64 rune string uint uint8 uint16 uint32 uint64 uintptr
- Hằng:
true false iota
- Giá trị zero:
nil
- Các hàm:
1 2
append cap close complex copy delete imag len make new panic print println real recover
Các ID được export
Một ID có thể được export để cho phép truy cập vào ID đó từ package khác. Điều kiện:
- Kí tự đầu tiên trong tên phải in hoa.
- ID đó đã được khai báo trong block của pakage hoặc nó là field name, method name.
Khai báo hằng
Cú pháp:
1
2
3
4
5
6
7
8
const Pi float64 = 3.14159 // Đầy đủ
const zero = 0 // Không có kiểu
// Khai báo nhiều biến một lúc
const (
size int64 = 1024
eof = -1
)
const a, b = 1, 2
Có thể dùng iota
để khai báo các giá trị tuần tự hiệu quả. iota
đại diện cho index của khai báo hằng khi khai báo nhiều hằng trong (), bắt đầu từ 0.
1
2
3
4
5
6
const (
a = 1 << iota // a = 1
b = 1 << iota // b = 2
c = 3 // c = 3
d = 1 << iota // d = 8
)
Khai báo kiểu
Ràng buộc một ID (type name) với một kiểu. Khai báo kiểu gồm 2 loại: khai báo alias và định nghĩa kiểu.
- Cú pháp:
1 2 3 4 5 6 7
type nodeList = []*Node // Khai báo alias type Point struct{x, y float64} // Định nghĩa kiểu // Khai báo nhiều kiểu một lúc type ( nodeList = []*Node Point struct{x, y float64} )
- Khai báo alias: ràng buộc ID với một kiểu cho trước
id = type
. - Định nghĩa kiểu: tạo ra một kiểu mới, riêng biệt, khác biệt với kiểu cấu thành nó
id type
.
Khai báo biến
Cú pháp: var <tên biến> <kiểu> = <giá trị>
Khai báo biến sẽ tạo một hoặc nhiều biến, ràng buộc ID vào, và cho nó kiểu và giá trị khởi tạo.
1
2
3
4
5
6
7
var i int
var x = 0
var x, y float64 = -1, -2
var (
i int
u, v = 2.0, 3.0
)
- Nếu không có giá trị khởi tạo, thì mặc định sẽ khởi tạo zero value.
- Nếu không khai báo kiểu, thì sẽ suy diễn kiểu từ giá trị khởi tạo.
- Không thể dùng
var i = nil
. - Compiler sẽ báo lỗi nếu như khai báo biến trong thân hàm mà không dùng.
Khai báo biến ngắn gọn
Cú pháp: <tên biến> := <giá trị>
. Giống như khai báo biến, không có từ khóa var và không cần khai báo kiểu, chỉ cần khai báo giá trị khởi tạo
1
2
i, j := 0, 10
_, y, _ := coord(p)
- Có thể khai báo lại các biến đã được khai báo trước đó, với điều kiện là cùng kiểu.
- Khai báo lại sẽ không tạo biến mới, mà là gán giá trị mới vào biến cũ.
- Chỉ xuất hiện trong các hàm, thường dùng để khai báo các biến tạm trong các
if, for, switch
.
Khai báo hàm
Cú pháp: func <tên hàm> <signature> <thân hàm>?
1
2
3
4
5
6
7
func min(x int, y int) int {
if x < y {
return x
}
return y
}
func flushICache(begin, end uintptr) // hiện thực ở ngoài
- Signature định nghĩa parameters và result.
- Thân hàm phải kết thúc bằng các terminating statements.
- Thân hàm có thể không có, và hàm đó sẽ được hiện thực ở ngoài.
Khai báo phương thức (method)
Cú pháp: func <receiver> <tên phương thức> <signature> <thân hàm>?
. Thuộc tính là một hàm có receiver. Khác ở hàm là có thêm ràng buộc với base type của receiver. Vì Go không có class nên đây là thay thế cho method của class trong OOP.
- Base type của receiver không là con trỏ hay interface, và phải được định nghĩa trong cùng package với method.
- Receiver là danh sách 1 tham số, có kiểu đã định nghĩa
T
(giá trị) hoặccon trỏ tới T
(con trỏ, thường dùng hơn để thay đổi trực tiếp các thuộc tính). - Kiểu của method là kiểu có hàm, với receiver là argument đầu tiên.
1 2 3 4 5 6 7 8
func (p *Point) Length() float64 { return math.Sqrt(p.x * p.x + p.y * p.y) } func (p *Point) Scale(factor float64) { p.x *= factor p.y *= factor } //
Expression
Expression trả về kết quả tính toán khi áp dụng các phép toán, hàm lên các toán hạng (operands).
Qualified ID
Là ID đi kèm với package name ở trước. Package phải được import từ trước. Cú pháp: <package name>.<ID>
Composite literals
Là giá trị của struct, array, slice, map và tạo giá trị mới mỗi khi được tính.
Function literals
Đại diện cho anonymous function. Cú pháp: func <Signature> <Body>
. Function literal có thể được gán vào biến, hoặc gọi trực tiếp.
1
2
f := func(x, y int) int { return x + y }
func(x, y int) int { return x + y } (1, 2)
Selector expression
Cú pháp: <ID>.<selector>
. Để lấy giá trị của thuộc tính selector
trong ID.
- Nếu
ID
là kiểu pointer / interface, và có giá trịnil
thìID.f
sẽ gây ra lỗi runtime. - Nếu
x
là con trỏ thìx.f == (*x).f
.
Method expression
Cú pháp: <receiver>.<tên method>()
Index expression
Slice expression
Gồm 2 loại: đơn giản và đầy đủ.
- Đơn giản:
a[low:high]
- Phức tạp:
a[low:high:max]
, giống như đơn giản nhưng có capacity làmax - low
.
Type assertions
Gọi hàm
Truyền tham số bằng …
Giúp truyền nhiều tham số vào hàm, trong khi khai báo hàm chỉ có 1 tham số (phải là tham số cuối). Tương đương với một mảng. Để truyền một mảng vào tham số kiểu này, sử dụng … ở sau tên mảng truyền vào.
1
2
3
4
5
func Greeting(prefix string, people ...string)
Greeting("nobody") // prefix = nobody
Greeting("hello", "A", "B", "C") // prefix = hello, people = []string{"A", "B", "C"}
s := []string{"A", "B"}
Greeting("goodbye", s...)
Toán tử
Toán tử giúp gom các toán hạng lại với nhau.
- Toán tử số học:
+ - * / % & | ^ &^ << >>
- Toán tử so sánh:
== != < <= > >=
- Toán tử luận lý:
&& || !
- Toán tử địa chỉ:
&a
trả về con trỏ tới a,*p
trả về giá trị được lưu tại địa chỉ trỏ bởi p. - Toán tử nhận (receive operator):
<-ch
trả về giá trị nhận (receive value) từ kênhch
.ch
phải có kiểu kênh (channel).
Ép kiểu
Cú pháp: <kiểu> ( <expression> )
. Ví dụ: int(a)
.
Statement
Statement giúp thay đổi trạng thái của chương trình.
Labeled
Cú pháp: ID: <Statement>
. Labeled statement là mục tiêu của goto
, break
và continue
.
Send
Cú pháp: <Channel> <- <Expression>
. Mục đích: gửi giá trị vào một channel.
IncDec
Cú pháp: <Expression> (++ / --)
. Mục đích: tăng/giảm giá trị đi 1.
Gán
Cú pháp: <Expression_list> (= / <op>=) <Expression_list>
. Cú pháp x <op>= y
đồng nghĩa với x = x <op> y
.
If
Cú pháp: If <Simple Stmt ;>? <Expression> <Block> else if ... else <Block>
. Trước expression của If có thể có một statement đơn giản để khởi tạo trước.
Switch
Thay vì if chỉ cho phép rẽ 2 nhánh (true / false), switch cho phép rẽ nhiều nhánh. Có 2 loại: switch theo expression và switch theo kiểu.
Cú pháp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Theo expression
switch x := f(); tag {
default: s3()
case 0, 1, 2, 3: s1()
case 4, 5, 6, 7: s2()
}
// Theo kiểu
switch i := x.(type) {
case nil:
printString("x is nil")
case int:
printInt(i)
case float64:
printFloat64(i)
case func(int) float64:
printFunction(i)
case bool, string:
printString("type is bool or string")
default:
printString("don't know the type")
}
For
Gồm 3 loại: điều kiện đơn, mệnh đề “for” và mệnh đề “range”.
- Điều kiện đơn: giống như while trong C. Cú pháp:
for a < b {}
- Mệnh đề for: giống như for trong C. Cú pháp:
for i:= 1; i < 2; i++ {}
- Mệnh đề range: giống như foreach trong PHP. Cú pháp:
for first, second := range range_exp {}
. Có 4 loại range expression, gồm:- Array/slice:
first
lài
(int),second
làa[i]
. - String:
first
lài
(int),second
là kí tự tạii
(rune). - Map (
map[K](V)
):first
là key,second
là value. - Channel:
first
là element, không cósecond
.
- Array/slice:
Go
Lệnh go
bắt đầu quá trình thực thi của một lệnh gọi hàm như một thread đồng thời, độc lập, hay còn gọi là goroutine
, trong cùng không gian địa chỉ. Cú pháp: go Server()
.
Select
Lệnh select
lựa chọn những phép send
hoặc receive
sẽ thực thi (dùng trong channel). Có cấu trúc khá giống như switch
, nhưng các case liên quan đến các phép communication.
Cú pháp:
1
2
3
4
5
6
7
8
select {
case i1 = <-c1:
print("received")
case c2 <- i2:
print("send")
default:
print("no communication")
}
Return
Dùng để kết thúc hàm, và trả về giá trị. Cú pháp như thông thường. Cách return trống: return
. Khi vào một hàm thì giá trị trả về luôn được khởi tạo là zero value.
Break
Dùng để thoát khỏi for
, switch
, select
gần nhất. Cú pháp: break
hoặc break Label
. Nếu có Label thì Label đó phải chứa lệnh for chứa lệnh break, thường dùng để thoát khỏi nhiều vòng lặp.
Continue
Dùng để nhảy đến iteration tiếp theo trong for
gần nhất. Cú pháp tương tự break
(có Label).
Goto
Fallthrough
Cú pháp: fallthrough
. Dùng để chuyển đến lệnh đầu tiên của case
tiếp theo trong switch
.
Defer
Cú pháp: defer <lệnh gọi hàm/phương thức>
. Dùng để hoãn lệnh gọi hàm/phương thức cho đến khi return hàm ngoài thì sẽ chạy.