Golang

ภาษา Golang, fundamental, lib, standard package

Golang ทำ CRUD Service APIs (No Database) พร้อมเขียน Test ด้วย

ช่วงนี้ผู้เขียนกำลังเริ่มเรียนรู้เกี่ยวกับ Golang มาสักระยะ วันนี้ก็เลยอยากจะพามาลงบทเรียนการเริ่มพัฒนา Service API ด้วยภาษา Go โดยบทความนี้จะขอ Coding โดยใช้ Echo Framework สำเร็จเพื่อรวดเร็วในการพัฒนา Web Service API มาเริ่มกันเลย

เตรียมความพร้อมก่อน

  • ต้องการ Go runtime เวอร์ชั่น v1.13 หรือมากกว่า สำหรับ Echo v.4
  • IDE หรือ Editor (VScode,Sublime,Atom) แล้วแต่ความถนัด (Vscode ติดตั้ง Extensions Golang)
  • Postman IDE หรือ Terminal ตามความถนัด เช่นกัน

เริ่มโค๊ดกันเลย

  • initial go project go mod init poolsawat.com-echo-crud
  • download echo framework libs
[email protected] poolsawat.com-echo-crud % go get github.com/labstack/echo/v4
go: added github.com/labstack/echo/v4 v4.9.0
go: added github.com/labstack/gommon v0.3.1
go: added github.com/mattn/go-colorable v0.1.11
go: added github.com/mattn/go-isatty v0.0.14
go: added github.com/valyala/bytebufferpool v1.0.0
go: added github.com/valyala/fasttemplate v1.2.1
go: added golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
go: added golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f
go: added golang.org/x/sys v0.0.0-20211103235746-7861aae1554b
go: added golang.org/x/text v0.3.7
  • สร้าง models/user.go
package models

type User struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}
  • สร้าง routes/user.go
package routes

import (
	"net/http"
	"strconv"

	"github.com/labstack/echo/v4"
	"poolsawat.com-echo-crud/models"
)

var (
	seq = 1
)

type RouteUserHandler struct {
	Users map[int]*models.User
}

//----------
// Handlers
//----------

func (h *RouteUserHandler) CreateUser(c echo.Context) error {
	u := &models.User{
		ID: seq,
	}
	if err := c.Bind(u); err != nil {
		return err
	}
	h.Users[u.ID] = u
	seq++
	return c.JSON(http.StatusCreated, u)
}

func (h *RouteUserHandler) GetUser(c echo.Context) error {
	id, _ := strconv.Atoi(c.Param("id"))
	return c.JSON(http.StatusOK, h.Users[id])
}

func (h *RouteUserHandler) UpdateUser(c echo.Context) error {
	u := new(models.User)
	if err := c.Bind(u); err != nil {
		return err
	}
	id, _ := strconv.Atoi(c.Param("id"))
	h.Users[id].Name = u.Name
	return c.JSON(http.StatusOK, h.Users[id])
}

func (h *RouteUserHandler) DeleteUser(c echo.Context) error {
	id, _ := strconv.Atoi(c.Param("id"))
	delete(h.Users, id)
	return c.NoContent(http.StatusNoContent)
}

func (h *RouteUserHandler) GetAllUsers(c echo.Context) error {
	return c.JSON(http.StatusOK, h.Users)
}
  • สร้าง server.go file กำหนดเป็น main package
package main

import (
	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
	"poolsawat.com-echo-crud/models"
	"poolsawat.com-echo-crud/routes"
)

func main() {
	e := echo.New()

	// Middleware
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())

	users := map[int]*models.User{}

	route := &routes.RouteUserHandler{users}

	// Routes
	e.GET("/users", route.GetAllUsers)
	e.POST("/users", route.CreateUser)
	e.GET("/users/:id", route.GetUser)
	e.PUT("/users/:id", route.UpdateUser)
	e.DELETE("/users/:id", route.DeleteUser)

	// Start server
	e.Logger.Fatal(e.Start(":1323"))
}
  • ทดสอบ Service APIs ทั้งหมด
  • [POST] /users
curl -X POST \
  -H 'Content-Type: application/json' \
  -d '{"name":"Poolsawat.com 001"}' \
  localhost:1323/users
  • [GET] /users
curl localhost:1323/users
  • [GET] /users/1
curl localhost:1323/users/1
  • [PUT] /users/1
curl -X PUT \
  -H 'Content-Type: application/json' \
  -d '{"name":"Poolsawat.com 001 Updated"}' \
  localhost:1323/users/1
  • [DELETE] /users/1
curl -X DELETE localhost:1323/users/1
  • สุดท้ายอย่าลืมเขียน Test routes/user_test.go
package routes_test

import (
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"poolsawat.com-echo-crud/routes"

	"github.com/labstack/echo/v4"
	"github.com/stretchr/testify/assert"
	"poolsawat.com-echo-crud/models"
)

func TestUsers(t *testing.T) {

	var (
		mockDB = map[int]*models.User{
			1: &models.User{1, "Poolsawat.com"},
		}
		extectedOne = "{\"id\":1,\"name\":\"Poolsawat.com\"}"
		extectedAll = "{\"1\":{\"id\":1,\"name\":\"Poolsawat.com\"}}"
	)

	t.Run(`should created user success`, func(t *testing.T) {
		// Setup
		e := echo.New()
		req := httptest.NewRequest(http.MethodPost, "/users", strings.NewReader(extectedOne))
		req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
		rec := httptest.NewRecorder()
		c := e.NewContext(req, rec)

		mockRoute := &routes.RouteUserHandler{mockDB}

		// Assertions
		if assert.NoError(t, mockRoute.CreateUser(c)) {
			assert.Equal(t, http.StatusCreated, rec.Code)
			assert.Equal(t, extectedOne, strings.TrimSpace(rec.Body.String()))
		}
	})

	t.Run(`should get all users success`, func(t *testing.T) {
		// Setup
		e := echo.New()
		req := httptest.NewRequest(http.MethodGet, "/users", nil)
		req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
		rec := httptest.NewRecorder()
		c := e.NewContext(req, rec)

		mockRoute := &routes.RouteUserHandler{mockDB}

		// Assertions
		if assert.NoError(t, mockRoute.GetAllUsers(c)) {
			assert.Equal(t, http.StatusOK, rec.Code)
			assert.Equal(t, extectedAll, strings.TrimSpace(rec.Body.String()))
		}
	})

	t.Run(`should get user by id success`, func(t *testing.T) {
		// Setup
		e := echo.New()
		req := httptest.NewRequest(http.MethodGet, "/users", nil)
		req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
		rec := httptest.NewRecorder()
		c := e.NewContext(req, rec)
		c.SetPath("/users/:id")
		c.SetParamNames("id")
		c.SetParamValues("1")

		mockRoute := &routes.RouteUserHandler{mockDB}

		// Assertions
		if assert.NoError(t, mockRoute.GetUser(c)) {
			assert.Equal(t, http.StatusOK, rec.Code)
			assert.Equal(t, extectedOne, strings.TrimSpace(rec.Body.String()))
		}
	})

	t.Run(`should update user success`, func(t *testing.T) {
		// Given
		expected := "{\"id\":1,\"name\":\"Poolsawat.com update\"}"

		// Setup
		e := echo.New()
		req := httptest.NewRequest(http.MethodPut, "/users", strings.NewReader(expected))
		req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
		rec := httptest.NewRecorder()
		c := e.NewContext(req, rec)
		c.SetPath("/users/:id")
		c.SetParamNames("id")
		c.SetParamValues("1")

		mockRoute := &routes.RouteUserHandler{mockDB}

		// Assertions
		if assert.NoError(t, mockRoute.UpdateUser(c)) {
			assert.Equal(t, http.StatusOK, rec.Code)
			assert.Equal(t, expected, strings.TrimSpace(rec.Body.String()))
		}
	})

	t.Run(`should delete user by id success`, func(t *testing.T) {
		// Setup
		e := echo.New()
		req := httptest.NewRequest(http.MethodDelete, "/users", nil)
		req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
		rec := httptest.NewRecorder()
		c := e.NewContext(req, rec)
		c.SetPath("/users/:id")
		c.SetParamNames("id")
		c.SetParamValues("1")

		mockRoute := &routes.RouteUserHandler{mockDB}

		// Assertions
		if assert.NoError(t, mockRoute.DeleteUser(c)) {
			assert.Equal(t, http.StatusNoContent, rec.Code)
			assert.Equal(t, "", strings.TrimSpace(rec.Body.String()))
		}
	})

}
  • รัน test ด้วย go test ./routes/user.go ./routes/user_test.go -v

ขอบคุณที่ติดตามครับ

Golang Program Flow Control EP5

Golang ก็เหมือนภาษาอื่น ๆ คือจะมี IF, ELSE, FOR LOOPS, SWITH CASE อื่น ๆ

IF, ELSE IF, ELSE คำสั่งหากินของทุก ๆ ภาษา เกือบจะถูกเรียกว่า AI

อธิบายรวดเดียวกันไปเลย ด้วยโค๊ดข้างล่างนี้

// if condition_that_evaluates_to_boolean{
//      perform action1
// }else if condition_that_evaluates_to_boolean{
//      perform action2
// }else{
//      perform action3
// }

price, inStock := 100, true

if price >= 80 { // parenthesis are no required to enclose the testing condition
    fmt.Println("Too Expensive")
}

if price <= 100 && inStock == true { //the same with: if price <= 100 && inStock { }
    fmt.Println("Buy it!")
}

// In Go there is not such a thing like the Truthiness of a variable.
// Error:
// if price {
//  fmt.Println("We have price!")
// }

// only one if branch will be executed
if price < 100 {
    fmt.Println("It's cheap!")
} else if price == 100 {
    fmt.Println("On the edge")
} else { //executed only once if all the if branches are false (it's optional)
    fmt.Println("It's Expensive!")
}

Simple IF

อธิบายรวดเดียวกันไปเลย ด้วยโค๊ดข้างล่างนี้ ง่ายดี

package main
 
import (
    "fmt"
    "strconv"
)
 
func main() {
 
    // converting string to int:
    i, err := strconv.Atoi("45")
 
    // error handling
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(i)
 
    }
 
    // simple (short) statement ->  the same effect as the above code
    // i and err are variables scoped to the if statement only
    if i, err := strconv.Atoi("34"); err == nil {
        fmt.Println("No error. i is ", i)
    } else {
        fmt.Println(err)
    }
}

For Loops การวนทำ ตามจำนวนข้อมูล

อธิบายรวดเดียวกันไปเลย ด้วยโค๊ดข้างล่างนี้ ง่ายดี


package main
 
import "fmt"
 
func main() {
 
    // printing numbers from 0 to 9
    for i := 0; i < 10; i++ {
        fmt.Println(i)
    }
 
    // has the same effect as a while loop in other languages
    // there is no while loop in Go
    j := 10
    for j >= 0 {
        fmt.Println(j)
        j--
    }
 
    // handling of multiple variables in a for loop
    for i, j := 0, 100; i < 10; i, j = i+1, j+1 {
        fmt.Printf("i = %v, j = %v\n", i, j)
    }
 
    // infinite loop
    // sum := 0
    // for {
    //  sum++
    // }
    // fmt.Println(sum) //this line is never reached
}

หากต้องการที่จะ break (หยุดการทำงาน) หรือ continue (ต่อเนื่อง จะข้ามรอบการทำงานนั้น) จะทำอย่างไร

 //** CONTINUE STATEMENT **//
 
// It works just the same as in C,  Java or Python.
// The continue statement rejects all the remaining statements in the current iteration of the loop
// and moves the control back to the top of the loop.


// printing even numbers less than or equal to 10
for i := 1; i <= 10; i++ {
    if i%2 != 0 {
        continue    // skipping the remaining code in this iteration
    }
    fmt.Println(i)
}


// **BREAK STATEMENT **//

// It is used to terminate the innermost for or switch statement.
// It works just the same as in C,  Java or Python.

// finding 10 numbers divisible by 13 
count := 0 
for i := 0; true; i++ {
    if i%13 == 0 {
        fmt.Printf("%d is divisible by 13\n", i)
        count++
    }

    if count == 10 { //if 10 numbers were found, break!
        break //it breaks the current loop (inner loop if there are more loops)
    }
}

// the break statement is not terminating the program entirely;
fmt.Println("Just a message after the for loop")

จะลืมได้ไง Switch Statement

package main
 
import "fmt"
 
func main() {
 
    language := "golang"
 
    switch language {
    case "Python": //values must be comparable (compare string to string)
        fmt.Println("You are learning Python! You don't use { } but indentation !! ")
        // an implicit break is added here
    case "Go", "golang": //compare language with "Go" OR "golang"
        fmt.Println("Good, Go for Go!. You are using {}!")
    default:
        // the default clause the equivalent of the else clause of an if statement
        // and gets executed if no testing condition is true.
        fmt.Println("Any other programming language is a good start!")
    }
 
    n := 5
    // comparing the result of an expression which is bool to another bool value
    switch true {
    case n%2 == 0:
        fmt.Println("Even!")
    case n%2 != 0:
        fmt.Println("Odd!")
    default:
        fmt.Println("Never here!")
    }
 
    //** Switch simple statement **//
 
    // Syntax: statement (n:=10), semicolon and a switch condition
    //(true in this case, we are comparing boolean expressions that return true)
    // we can remove the word "true" because it's the default
    switch n := 10; true {
    case n > 0:
        fmt.Println("Positive")
    case n < 0:
        fmt.Println("Negative")
    default:
        fmt.Println("Zero")
    }
}

Golang Data Types และ Operators EP4

Data Types ก็คล้าย ๆ เหมือนภาษาอื่น ๆ

  • NUMERIC TYPES ภายใต้ type นี้จะย่อยอีกเยอะมาก อธิบายตามโค๊ดข้างล่าง
    • uint เก็บค่าตั้งแต่จำนวนที่เป็นบวก รวมถึง 0
    • int เก็บค่า เป็นลบ 0 และบวก
    • float เก็บค่าที่รูปแบบทศนิยม
    • byte นามแผงของ uint8
    • rune นามแผงของ int32
// uint8      the set of all unsigned  8-bit integers (0 to 255)
// uint16      the set of all unsigned 16-bit integers (0 to 65535)
// uint32      the set of all unsigned 32-bit integers (0 to 4294967295)
// uint64      the set of all unsigned 64-bit integers (0 to 18446744073709551615)

// int8        the set of all signed  8-bit integers (-128 to 127)
// int16       the set of all signed 16-bit integers (-32768 to 32767)
// int32       the set of all signed 32-bit integers (-2147483648 to 2147483647)
// int64       the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)

// uint     either 32 or 64 bits
// int      same size as uint

// float32     the set of all IEEE-754 32-bit floating-point numbers
// float64     the set of all IEEE-754 64-bit floating-point numbers
// complex64   the set of all complex numbers with float32 real and imaginary parts
// complex128  the set of all complex numbers with float64 real and imaginary parts

// byte        alias for uint8
// rune        alias for int32

ตัวอย่างโค๊ด Numeric Types รูปแบบต่าง ๆ

//int type
var i1 int8 = -128     //min value
fmt.Printf("%T\n", i1) // => int8

var i2 uint16 = 65535  //max value
fmt.Printf("%T\n", i2) // => int16

var i3 int64 = -324_567_345  // underscores are used to write large numbers for a better readability
fmt.Printf("%T\n", i3)       // => int64
fmt.Printf("i3 is %d\n", i3) // => i3 is -324567345 (underscores are ignored)

//float64 type
var f1, f2, f3 float64 = 1.1, -.2, 5. // trailing and leading zeros can be ignored
fmt.Printf("%T %T %T\n", f1, f2, f3)

//rune type
var r rune = 'f'
fmt.Printf("%T\n", r) // => int32 (rune is an alias to int32)
fmt.Printf("%x\n", r) // => 66,  the hexadecimal ascii code for 'f'
fmt.Printf("%c\n", r) // => f
  • bool type เป็นได้แค่ true, false
  • string ข้อความ ต่างๆ หลาย ๆ ตัวอักษรรวมเป็น string
//bool type
var b bool = true
fmt.Printf("%T\n", b) // => bool

//string type
var s string = "Hello Go!"
fmt.Printf("%T\n", s) // => string

Array vs Slice Types

  • Array เป็น Data Type ที่กำหนดขนาดชัดเจน กำหนด size
  • Slice เป็น Data Type ที่ไม่กำหนดขนาด
//array type
var numbers = [4]int{4, 5, -9, 100}
fmt.Printf("%T\n", numbers) // =>  [4]int

//slice type
var cities = []string{"London", "Bucharest", "Tokyo", "New York"}
fmt.Printf("%T\n", cities) // => []string

Map การเก็บค่าข้อมูลโดยการกำหนด Key: Value

  • Key ของ Map จะ unique ไม่มีทางซ้ำกันได้
  • Value สามารถกำหนดได้ โดยทุก elements จะต้องเป็น Data Type แบบเดียวกัน
//map type
balances := map[string]float64{
    "USD": 233.11,
    "EUR": 555.11,
}
fmt.Printf("%T\n", balances) // => map[string]float64

Struct การกำหนดรูปแบบที่คล้ายการทำ Model, JPA Class ในภาษา Java

  • สามารถกำหนดตั้งชื่อได้เอง กำหนด properties ได้ไม่จำกัด แต่ละ property จะกำหนดด้วย Data Types ที่แตกต่างกันได้
//struct type
type Person struct {
    name string
    age  int
}
var you Person
fmt.Printf("%T\n", you) // => main.Person

Pointer Type เรื่องที่เข้าใจยาก ในบรรดาทุก Data Types

  • pointer คือตัวชี้ตำแหน่ง ถ้าใน GO ก็จะเป็นตำแหน่งการเก็บข้อมูลโดยจะไม่สามารถซ้ำๆ กับ address อื่น ได้
  • จะใช้สัญลักษณ์ * เพื่อบอกให้รู้ว่ากำลังกำหนด Type แบบ Pointer
  • โดยจะใช้ควบคู่กับ address ที่จะเป็นการบอกให้รู้ถึงที่อยู่ของ pointer นั้น ๆ จะใช้สัญลักษณ์ &
//pointer type
var x int = 2
ptr := &x                                                 // pointer to int
fmt.Printf("ptr is of type %T with value %v\n", ptr, ptr) // => ptr is of type *int with value 0xc000016168

Function Type

  • GO จะถือว่า function เป็น type รูปแบบนึงคล้าย javascript
//function type
fmt.Printf("%T\n", f) // => func()

func f() {
}

Operators อธิบายสั้น ๆ ด้วยโค๊ดข้างล่าง

a, b := 10, 5.5

//** ARITHMETIC OPERATORS **//
//  +       sum
// -        difference
// *        product
// /        quotient
// %        remainder
// there is no power operator in Go. Use math.Pow(a, b) for raising to a power.

fmt.Println(a + 5)   // => 15
fmt.Println(3.1 - b) // => -2.4
fmt.Println(a * a)   // => 100
fmt.Println(a / a)   // => 1
fmt.Println(11 / 5)  // => 2

// Go is a Strong Typed Language
// fmt.Println(a * b)       // =>  invalid operation: a * b (mismatched types int and float64)
fmt.Println(a * int(b))     // => 50
fmt.Println(float64(a) * b) // => 55

// IncDec Statements
// The "++" and "--" statements increment or decrement their operands by the untyped constant 1.
x := 10
x++ // x is 11. Same as: x += 1
x-- // x is 10. Same as: x -= 1

//** ASSIGNMENT OPERATORS **//
//  =   (simple assignment)
// +=   (increment assignment)
// -=   (decrement assignment)
// *=   (multiplication assignment)
// /=   (division assignment)
// %=   (modulus assignment)

a = 10
a += 2 // => a is 12
a -= 3 // => a is 9
a *= 2 // => a is 18
a /= 3 // => a is 6
a %= 5 // => a is 1

//** COMPARISON OPERATORS **//
//  ==      equal values
// !=       not equal
// >        left operand is greater than right operand
// <        left operand is less than right operand
// >=       left operand is greater than or equal to right operand
// <=       left operand is less than or equal to right operand

fmt.Println(5 == 6)   // => false
fmt.Println(5 != 6)   // => true
fmt.Println(10 > 10)  // => false
fmt.Println(10 >= 10) // => true
fmt.Println(5 < 5)    // => false
fmt.Println(5 <= 5)   // => true

//** LOGICAL OPERATORS **//
// &&       logical and
// ||       logical or
// !        logical negation

fmt.Println(0 < 2 && 4 > 1) // => true
fmt.Println(1 > 5 || 4 > 5) // => false
fmt.Println(!(1 > 2))       // => true

Converting Types

  • การแปลง Type ของ Data นึง ไปเป็นอีก Type Data นึง เช่น int -> string, string -> int
var x = 3   //int type
var y = 3.2 //float type

// x = x * y //compile error ->  mismatched types

x = x * int(y) // converting float64 to int
fmt.Println(x) // => 9

y = float64(x) * y //converting int to float64
fmt.Println(y)     // => 28.8

x = int(float64(x) * y)
fmt.Println(x) // => 259

//In Go types with different names are different types.
var a int = 5   // same size as int64 or int32 (platform specific)
var b int64 = 2 // int and int64 are not the same type

// a = b // error: cannot use b (type int64) as type int in assignment
a = int(b) // converting int64 to int (explicit conversion required)

// preventing unused variable error
_ = a

//** CONVERTING NUMBERS TO STRINGS AND STRINGS TO NUMBERS **//

s := string(99)            // int to rune (Unicode code point)
fmt.Println(s)             // => 99, the ascii code for symbol c
fmt.Println(string(34234)) // => 34234 is the unicode code point for 薺

// we cannot convert a float to a string similar to an int to a string
// s1 := string(65.1) // error

// converting float to string
var myStr = fmt.Sprintf("%f", 5.12)
fmt.Println(myStr) // => 5.120000

// converting int to string
var myStr1 = fmt.Sprintf("%d", 34234)
fmt.Println(myStr1) // => 34234

// converting string to float
var result, err = strconv.ParseFloat("3.142", 64)
if err == nil {
    fmt.Printf("Type: %T, Value: %v\n", result, result) // => Type: float64, Value: 3.142
} else {
    fmt.Println("Cannot convert to float64!")
}

// Atoi(string to int) and Itoa(int to string).
i, err := strconv.Atoi("-50")
s = strconv.Itoa(20)
fmt.Printf("i Type is %T, i value is %v\n", i, i) // => i Type is int, i value is -50
fmt.Printf("s Type is %T, s value is %q\n", s, s) // => s Type is string, s value is "20"

Defined Types กำหนดชื่อ Type ใหม่

  • กำหนดชื่อของ Type ที่สื่อความหมายเฉพาะ
package main
 
import "fmt"
 
type age int        //new type, int is the underlying type
type oldAge age     //new type, int (not age) is the underlying type
type veryOldAge age //new type, int (not age) is the underlying type
 
func main() {
 
    // new type speed (underlying type uint)
    type speed uint
 
    // s1, s2 of type speed
    var s1 speed = 10
    var s2 speed = 20
 
    // performing operations with the new types
    fmt.Println(s2 - s1) // -> 10
 
    // uint and speed are different types (they have different names)
    var x uint
 
    // x = s1  //error different types
 
    // correct
    x = uint(s1)
    _ = x
 
    // correct
    var s3 speed = speed(x)
    _ = s3
}

Golang ทำไม package fmt ถึงสำคัญสำหรับผู้เริ่มต้น EP3

package fmt คืออะไร

ใช้ I/O ที่จัดรูปแบบด้วยฟังก์ชันที่คล้ายคลึงกับ printf และ scanf ของภาษา C รูปแบบการกร มาจาก C แต่ง่ายกว่า

fmt.Println

  • การ Print แบบขึ้นบรรทัดใหม่ ด้วย fmt.Println(“message”) //=> message
  • การ Print หลาย args แบบขึ้นบรรทัดใหม่ fmt.Println(“arg0 := “,10,”, arg1 := “,99) // => arg0 := 10, arg1 := 99
 fmt.Println("Hello Go World!") // => Hello Go World!

fmt.Printf

  • การ Print ที่จะสามารถกำหนด format ของ message ได้โดย format ที่กำหนดมีอยู่ด้วยกันหลายรูปแบบ เช่น
    • %d -> decimal
    • %f -> float
    • %s -> string
    • %q -> double-quoted string
    • %v -> value (any)
    • %#v -> a Go-syntax representation of the value
    • %T -> value Type
    • %t -> bool (true or false)
    • %p -> pointer (address in base 16, with leading 0x)
    • %c -> char (rune) represented by the corresponding Unicode code poi
 fmt.Printf()

ตัวอย่างการใช้งาน fmt.Printf

a, b, c := 10, 15.5, "Gophers"
grades := []int{10, 20, 30}
 
fmt.Printf("a is %d, b is %f, c is %s \n", a, b, c)    // => a is 10, b is 15.500000, c is Gophers
fmt.Printf("%q\n", c)                      // => "Gophers"
fmt.Printf("%v\n", grades)                 // => [10 20 30]
fmt.Printf("%#v\n", grades)                // => b is of type float64 and grades is of type []int
fmt.Printf("b is of type %T and grades is of type %T\n", b, grades) 
    // => b is of type float64 and grades is of type []int
fmt.Printf("The address of a: %p\n", &a)    // => The address of a: 0xc000016128
fmt.Printf("%c and %c\n", 100, 51011)       // =>  d and 읃  (runes for code points 101 and 51011)

แต่ถ้าอยากที่จะขึ้นบรรทัดใหม่ด้วยก็ให้ เพิ่ม \n ใน fmt.Printf(“%q\n”, c) ก็จะได้การขึ้นบรรทัดใหม่ด้วย

fmt.Sprintf

ใช้งานเหมือน Printf แต่จะ return เป็น string

a, b, c := 10, 15.5, "Gophers"
// fmt.Sprintf() returns a string. Uses the same verbs as fmt.Printf()
s := fmt.Sprintf("a is %d, b is %f, c is %s \n", a, b, c)
fmt.Println(s) // => a is 10, b is 15.500000, c is Gophers

Golang เรียนนรู้ Syntax ต่าง ๆ ของ GO EP2

Variables and Declarations

ตัวแปร และการประกาศค่า หลัก ๆ
  • ที่เห็นได้ชัดเจน จะมี 2 รูปแบบ คือกำหนด type ชัดเจน เช่น var s1 string และให้แปลง type ให้ตามข้อมูลที่ assign เริ่มต้น เช่น var s1 = “poolsawat.com” // จะได้ type string
  • การกำหนดค่าหลาย ๆ ค่า ก็สามารถ assings แบบนี้ var i, j int = 1, 2 ได้เช่นกัน
  • การประกาศค่าแบบสั้น ด้วย s1 := “poolsawat.com”
  • อื่น ๆ ตามโค๊ดตัวอย่างข้างล่าง
/////////////////////////////////
// Variables and Declarations
// Go Playground: https://play.golang.org/p/PKdAxUp8mNT
/////////////////////////////////
 
package main
 
import "fmt"
 
func main() {
 
    //** DECLARING VARIABLES **///
 
    // Syntax: var var_name type
    var s1 string
    s1 = "Learning Go!"
    fmt.Println(s1) // printing string s1
 
    //** TYPE INFERENCE **//
 
    // Go deduces automatically the type of the variable by looking at the initial value (bool, int, string etc)
 
    var k int = 6 // not necessary to say the type (int). It is inferred from the literal on the right side of =
    var i = 5     // type int
    var j = 5.6   // type float64
 
    // printing i, j and k
    fmt.Println("i:", i, "j:", j, "k:", k)
 
    // ii == jj  // -> error: cannot assign float to int (Go is a strong typed language)
 
    // declaring and initializing a new variable of type string (type inference)
    var s2 = "Go!"
    _ = s2 //in Go each variable must be used or there is a compile-time error
    // _ is the Blank Identifier and mutes the error of unused variables
    // _ can be only on the left hand side of the = operator
 
    // multiple assignments
    var ii, jj int
    ii, jj = 5, 8 // -> tuple assignment. It allows several variables to be assigned at once
 
    // swapping two variables using multiple assignments
    ii, jj = jj, ii
 
    fmt.Println(ii, jj)
 
    //** Short Declaration (works only in Block Scope!) **//
 
    // := (colon equals syntax) used only when declaring a new variable (or at least a new variable)
    // := tells go we are going to create a new variable and go figures out what type it will be
    s3 := "Learning golang!"
    _ = s3
 
    // can't use short declaration at Package Scope (outside main() or other function)
    // all statements at package scope must start with a Go keyword (package, var, import, func etc)
 
    // multiple short declaration
    car, cost := "Audi", 50000
    fmt.Println(car, cost)
 
    // redeclaration with short declaration syntax
    // at least one variable must be NEW on the left side of :=
    var deleted = false
    deleted, file := true, "a.txt"
    _, _ = deleted, file
 
    // expressions in short declarations are allowed
    sum := 5 + 2.5
    fmt.Println(sum)
 
    // multiple declaration is good for readability
    var (
        age       float64
        firstName string
        gender    bool
    )
    _, _, _ = age, firstName, gender
 
    // a concise way to declare multiple variables that have the same type
    var a, b int
    _, _ = a, b
 
}

Types and Zero Values

ชนิดของตัวแปร และค่า 0 (ศูนย์)

  • ตัวอย่าง type ของ GO เช่น string, int, float และอื่น ๆ เพิ่มเติม
  • หากไม่ initial value ให้ GO จะ set default value ให้ int // initialized with 0, float //initialized with 0.0,bool //initialized with false และ string //initialized with empty string
  • การตรวจสอบ type ของตัวแปร สามารถทำได้หลายวิธี 1 ในวิธีที่ง่าย คือ %T ตัวอย่าง fmt.Printf(“The type of name is: %T\n”, name)
/////////////////////////////////
// Types and Zero Values
// Go Playground: https://play.golang.org/p/zItROROXi64
/////////////////////////////////
 
package main
 
import "fmt"
 
func main() {
 
    // you must provide a type for each variable you declare or Go should be able to infer it
    var a int = 10
    var b = 15.5      // type inference (deduction)
    c := "Gopher"     // short declaration, type inference
    _, _, _ = a, b, c // Blank Identifier (_) to get rid of unused variable error
 
    // Go is a Statically and Strong Typed Programming Language
    // a = 3.14 -> error. A variable cannot change it's type
    // a = b    -> error. It's not allowed to assign a type to another type
 
    //** ZERO VALUES **//
 
    // An uninitialized variable or empty variable  will get the so called ZERO VALUE
    // The zero-value mechanism of Go ensures that a variable always holds a well defined value of its type
    var value int                         // initialized with 0
    var price float64                     // initialized with 0.0
    var name string                       // initialized with empty string -> ""
    var done bool                         // initialized with false
    fmt.Println(value, price, name, done) // -> 0 0.0 ""  false
}

Comments and Naming Convention

การ comment code และการตั้งชื่อตัวแปร และค่าอื่น ๆ
  • comment บรรทัดเดียว จะใช้ // my code un use
  • comments หลาย ๆ บรรทัด /* my code un use multi lines */
  • การตั้งชื่อไม่ควรยาวเกินความจำเป็น ต้องสื่อกับหน้าที่ของชนิดนั้น ๆ และต้องง่านต่อการดูในภายหลัง
  • ไม่ควรตั้งชื่อที่ใช้ _ (underscore) ในการตั้งชื่อ
/////////////////////////////////////////
// Comments and Naming Conventions in Go
// Go Playground: https://play.golang.org/p/pprI80SPMkS
/////////////////////////////////////////
 
package main
 
//** COMMENTS **//
 
// this is a single line comment
 
/*
 This is a block comment.
 a := 10
 fmt.Println(a)
*/
 
var name = "John Wick" // inline comment
 
//** NAMING CONVENTIONS IN GO **//
 
// Naming Conventions are important for code readability and maintainability.
 
// use short, concise names especially in shorter scopes
// common names for common types:
var s string   //string
var i int      //index
var num int    //number
var msg string //message
var v string   //value
var err error  //error value
var done bool  //bool, has been done?
 
// use mixedCase a.k.a camelCase instead of snake_case (variables and  functions)
var maxValue = 100  // recommended (camelCase)
var max_value = 100 // not recommended (snake_case)
 
// recommended
func writeToFile() {
}
 
// not recommended
func write_to_file() {
}
 
// write acronyms in all caps
var writeToDB = true // recommended
var writeToDb = true // not recommended
 
func main() {
 
    // use fewer letters, don’t be too verbose especially in smaller scopes
    var packetsReceived int // NOT OK, too verbose
    var n int               // OK
    _, _ = packetsReceived, n
 
    // an uppercase first letter has special significance to go (it will be exported in other packages)
}

Golang เริ่มต้น Golang Developer EP1

สวัสดีครับ พอมีเวลาว่าง ๆ เริ่มศึกษาภาษาใหม่ ๆ ก็อยากที่จะทำ series ของ Golang ไว้กับชุดบทความนี้

ติดตั้ง golang และ ทำความรู้จัก GOROOT, GOPATH

  • download golang version ล่าสุด https://golang.org/dl/
  • หลังจากติดตั้ง golang แล้ว ตรวจสอบเวอร์ชั่น ด้วย ‘go version
  • GOROOT กำหนด go compile path ด้วยคำสั่ง export GOROOT=/usr/local/go
  • GOPATH กำหนด go path ของ project ด้วย export GOPATH=$HOME/go
  • set PATH หลัก ด้วย export PATH=$GOPATH/bin:$GOROOT/bin:$PATH

เริ่ม Print “Hello Golang”

  • สร้าง directory เพื่อเก็บ code ของ workshop นี้ไว้ที่ “$GOPATH/src/master_go_programming/hello_world
  • สร้างไฟล์ “main.go” ใส่โค๊ดนี้ในไฟล์ main.go
package main

import "fmt"

func main() {

    fmt.Println("Hello Golang")

}
  • สั่ง go run เพื่อ compile main.go ด้วย ‘go run main.go’

Editor VScode และ extensions