Go Type System
Because Go is a static type system, type checking occurs at build time.
Dynamic type systems, on the other hand, such as JavsScript, can postpone type checking until runtime.
Refer to this article.
The Go Type System is designed to catch bugs as early as possible.
Before we go into types in Go, we should first learn about bits and bytes.
Bit
A bit can be either 0 or 1.
We can use bits to encode information, for example, 0 is 'false' and 1 is 'true'.
The number of bits in 1 bit is 2 (2 to the power of 1).
fmt.Printf("%b\n", 0)
fmt.Printf("%b\n", 1)
0
1
We can utilize more bits to represent more information.
The amount of information that may be obtained from two bits is four (2 to the power of two).
fmt.Printf("%02b\n", 0)
fmt.Printf("%02b\n", 1)
fmt.Printf("%02b\n", 2)
fmt.Printf("%02b\n", 3)
Result:
00
01
10
11
More Details about "%02b" format from the example above:
* b: base 2
* 0: starting with 0 if needed
* 2: show 2 digits
Byte
A byte is made up of 8 bits.
Bytes, as opposed to bits, represent the smallest and most meaningful piece of information.
1 byte = 8 bits = 256 possible values (2 to the power of 8)
The term 'byte' refers to a numeric data type.
var b1 byte
b1 = 0
fmt.Printf("%08b - %v\n", b1, b1)
var b2 byte
b2 = 255
fmt.Printf("%08b - %v\n", b2, b2)
Result:
00000000 - 0
11111111 - 255
Predeclared Types
Predeclared types are built-in types that can be used anywhere.
* bool => true or false => 1 byte (the minimum object is byte)
* int => based on your pc, it could be int32 or int64 (modern)
* int8 => -128 to 127
* int16 => -32768 to 32767
* int32 => -2 billions to +2 billions
* int64 => -9 quintillions to +9 quintillions
* uint => based on your pc, it could be uint32 or uint64 (modern)
*uint8 => 0 to 255
* uint16 => 0 to 65535
* uint32 => 0 to 4 billions
* uint64 => 0 to 18 quintillions
* float32
* float64
* string => []byte
The number after type means bit-size, which define the range of value a type can represent.
Overflow
Consider the following example if the value exceeds the maximum.
// range: 0 to 255
var counter uint8 = 255
fmt.Println("old: ", counter)
counter++
fmt.Println("new: ", counter)
Result:
old: 255
new: 0
If there is an overflow, the value is reset to its minimum.
Because it is an unsigned integer, it will be reset to 0.
Let's see if it's a signed integer.
var counter int8 = math.MaxInt8
fmt.Println("old: ", counter)
counter++
fmt.Println("new: ", counter)
Result:
old: 127
new: -128
You can check 'integer overflow' to get more info.
exp/mian.go
operations: +-*/%
representation: -1, 0, 1
size: 8 bytes
Defined Types (Named Types)
Consider the following example.
It includes methods for converting between Celsius and Fahrenheit.
exp/mian.go
package main
import "fmt"
func convertCelsiusToFahrenheit(celsis float64) float64 {
return (celsis * 9 / 5) + 32
}
func convertFahrenheitToCelsius(fahrenheit float64) float64 {
return (fahrenheit - 32) * 5 / 9
}
func main() {
var celsius float64 = 100
var fahrenheit float64 = 212
fmt.Printf("celsis: %f, ferenheit: %f\n", celsius, convertCelsiusToFahrenheit(celsius))
fmt.Printf("celsis: %f, ferenheit: %f\n", convertFahrenheitToCelsius(fahrenheit), fahrenheit)
}
However, because both methods take float64 input, it is a nightmare to identify this error if the wrong function is invoked.
Is there any way we can increase 'type safety'?
To avoid it, Go offers a defined type (also known as a named type). Refer to this doc.
A defined type can only be created from an already existing type.
Consider the Duration type in a time package as an example.
type Duration int64
Duration is a specific type.
And specified types inherit attributes from real-structured types.
The underlying type is int64.
int64 is a valid data type. It is organized.
representation: -1, 0, 1
size: 8 bytes
However, a defined type and its underlying type are distinct kinds.
As a result, they cannot be used in the same expression without being converted.
var totalSeconds time.Duration = 1000000000
var secondToAdd int64 = 1000000000
fmt.Println(totalSeconds + secondToAdd)
Error:
invalid operation: totalSeconds + secondToAdd (mismatched types time.Duration and int64)
If two types share the same underlying type, they can be converted to each other.
var totalSeconds time.Duration = 1000000000
var secondToAdd int64 = 1000000000
fmt.Println(totalSeconds + time.Duration(secondToAdd))
Result:
2s
Then we can modify our software as shown below.
temperature/temperature.go
package temperature
// Celsius ...
type Celsius float64
// Fahrenheit ...
type Fahrenheit float64
// ConvertCelsiusToFahrenheit ...
func ConvertCelsiusToFahrenheit(celsis Celsius) float64 {
return float64(celsis)*9/5 + 32
}
// ConvertFahrenheitToCelsius ...
func ConvertFahrenheitToCelsius(fahrenheit Fahrenheit) float64 {
return (float64(fahrenheit) - 32) * 5 / 9
}
temperature/cmd/main.go
package main
import (
"fcheng/temperature"
"fmt"
)
func main() {
var celsius temperature.Celsius = 100
var fahrenheit temperature.Fahrenheit = 212
fmt.Printf("celsis: %f, ferenheit: %f\n", celsius,
temperature.ConvertCelsiusToFahrenheit(celsius))
fmt.Printf("celsis: %f, ferenheit: %f\n",
temperature.ConvertFahrenheitToCelsius(fahrenheit), fahrenheit)
}
Other advantages of using defined types include:
* Improved readability
We can tell that 'Duration' refers to time rather than guessing what int64 is.
* Increase type safety
As in the last temperature example.
* Make available customized ways
Aliased Types
It is the same type as before, but with a different name.
It enhances readability and eliminates the need to transform the type.
For example, byte and uint8 are identical types with different names.
In addition, for greater readability, we recommend byte over uint8.
The following code executes without error.
var byteValue byte
var uint8Value uint8
uint8Value = byteValue
fmt.Println(uint8Value)
No comments:
Post a Comment