A data type in Go is the rule that indicates what values a variable can store and what operations we can perform with it.
We already know how to declare variables, but… what can we store in them?
Go is a statically and strongly typed language. This means two things:
- Static: The compiler needs to know the type of each variable before compiling.
- Strong: The compiler will not perform implicit conversions for you. You cannot add a number and a text without explicitly telling it how to do it.
Let’s look at the basic predeclared types with which we will build everything else.
Integers
In Go, integers are divided into two families: signed (int) and unsigned (uint).
Unlike languages like JavaScript (which only has Number for everything), in Go we have precise control over the memory size of our numbers.
Architecture-dependent types
These are the ones we will use 90% of the time.
int: 32 or 64 bits depending on the architecture we compile for.uint: same size asint, but represents unsigned numbers, including zero.
Although on your machine int is 64 bits, for Go int and int64 are different types. You cannot assign them directly without a conversion.
Fixed-size types
If you need to save memory or interact with hardware/binary protocols, you will use these:
| Type | Description | Approximate Range |
|---|---|---|
int8 | 8 bits | -128 to 127 |
int16 | 16 bits | -32768 to 32767 |
int32 | 32 bits | -2 billion to 2 billion |
int64 | 64 bits | −9.22 × 10¹⁸ to 9.22 × 10¹⁸ |
uint8 | 8 bits (unsigned) | 0 to 255 |
Important aliases: byte and rune
In Go you will very often see two types that are actually aliases for integers, but are used with a semantic intention:
byte: An alias foruint8. Used to represent raw binary data or ASCII characters.rune: An alias forint32. Used to represent a Unicode Code Point.
var a int = 10
var b uint8 = 255
var c rune = 'Ñ' // A rune is defined with single quotesIn Go there is no char type. We use rune to represent a Unicode code point, which can occupy more than one byte in UTF-8. A visual character can be made up of several runes, something common in compound emojis.
Floating Point Numbers
For numbers with decimals, Go simplifies things and offers us two options under the IEEE-754 standard:
float32: Single precision.float64: Double precision.
var pi float32 = 3.14
var euler float64 = 2.7182818284Which one to use?
By default, Go infers float64 when you write a decimal (num := 10.5). We recommend always using float64 unless you have a very specific memory constraint (such as in 3D graphics), since float32 accumulates rounding errors very quickly.
Booleans
The bool type is the simplest. It can only be true or false.
The important thing here is that Go does not allow implicit conversions.
- In C or C++,
0is false and1is true. - In Go,
0is a number andfalseis a boolean. They don’t mix.
var active bool = true
// if 1 { ... } <-- ERROR: "non-bool used as if condition"
// if active { ... } <-- CORRECTStrings
Internally, a string in Go is an immutable sequence of bytes. It can contain arbitrary bytes; by convention we usually store UTF-8 text, and source code literals are written with that encoding.
Declaration
We can use double quotes or backticks:
// Normal string (interprets escape characters like \n)
greeting := "Hello\nWorld"
// Raw String Literal (ignores escapes, allows multiline)
html := `
<html>
<body>Hello</body>
</html>
`Immutability
In Go, you cannot modify a string once created.
s := "Cat"
s[0] = 'R' // ERROR: cannot assign to s[0]If you want to change a string, you have to create a new one or convert it to a slice of rune or byte, modify it, and convert it back.
Review of Zero Values
As mentioned in the previous article, Go guarantees that every variable has an initial value. This is important for memory safety. Here is the reference table for basic types:
| Type | Zero Value |
|---|---|
Integers, float32, float64, byte, rune | 0 |
bool | false |
string | "" (empty string, NOT nil) |
It is important to note that an empty string has length 0, but it is not nil.
Type Conversions
Go does not implicitly convert between different numeric types.
If you want to add an int and a float64, you have to convert one of them explicitly. The syntax is Type(value).
package main
import "fmt"
func main() {
var integer int = 42
var decimal float64 = 55.5
// sum := integer + decimal // ERROR: mismatched types int and float64
// Option A: Everything to float
sum := float64(integer) + decimal
// Option B: Everything to int (losing decimals)
intSum := integer + int(decimal)
fmt.Println(sum) // 97.5
fmt.Println(intSum) // 97
}This may seem verbose at first, but it makes the conversion visible. It does not in itself prevent precision loss, truncation, or overflow: before converting we must check that the value fits in the destination type.