HowtoGo
Home / Basics / Types

A custom type built on an existing type gets its own identity and its own methods. It's how a plain string or int becomes something the compiler treats as distinct.

There are a few ways to declare variables (and types) in Go. Here we print the Type and default value for each.

var active bool = true
var count int = 42
var price float64 = 19.99
name := "Go"

fmt.Printf("%T: %v\n", active, active)
fmt.Printf("%T: %v\n", count, count)
fmt.Printf("%T: %v\n", price, price)
fmt.Printf("%T: %v\n", name, name)

%T prints the underlying type of a value and %v prints the value itself.

Terminal
$ go run main.go
bool: true
int: 42
float64: 19.99
string: Go

Making your own types

Examples

type Name string declares a new, distinct type with string as its underlying type. Name and string aren't interchangeable without a conversion, even though they share the same representation.

type Name string

n := Name("Ferris")
fmt.Println(n)
Output
Ferris

Every type in Go

Every type below is either predeclared, meaning its name is always in scope, or a type literal you write out yourself. There is nothing else.

TypeSizeRange or formNotes
Booleans
bool1 bytetrue / falseThe only type an if or for condition accepts.
Signed integers
int88-bit−128 to 127
int1616-bit−32,768 to 32,767Rare outside binary formats.
int3232-bit−2,147,483,648 to 2,147,483,647rune is an alias for this.
int6464-bit−9,223,372,036,854,775,808 to 9,223,372,036,854,775,807What time.Duration is built on.
int32 or 64-bitSame as int32 or int64, whichever the platform usesThe default for whole numbers. Use it unless you need a fixed width.
Unsigned integers
uint88-bit0 to 255byte is an alias for this.
uint1616-bit0 to 65,535
uint3232-bit0 to 4,294,967,295
uint6464-bit0 to 18,446,744,073,709,551,615
uint32 or 64-bitSame as uint32 or uint64Subtracting past 0 wraps to a huge number rather than going negative.
uintptrpointer-sizedLarge enough to hold any pointer's bitsFor unsafe pointer arithmetic. Not a pointer the collector tracks.
Aliases
byte8-bitIdentical to uint8An alias, not a separate type. Used when the bytes are data, not numbers.
rune32-bitIdentical to int32An alias. Holds one Unicode code point.
Floating point
float3232-bit±1.18e−38 to ±3.40e38About 7 decimal digits of precision.
float6464-bit±2.23e−308 to ±1.80e308About 15 digits. The default for decimals.
Complex
complex6464-bitTwo float32 valuesReal and imaginary parts.
complex128128-bitTwo float64 valuesThe default for complex literals.
Strings
string16-byte headerImmutable bytes, conventionally UTF-8Indexing gives a byte. Ranging gives a rune.
Predeclared interfaces
errorinterfaceAny type with Error() stringAn ordinary interface, not built into the language.
anyinterfaceAlias for interface{}Added in Go 1.18. Every type satisfies it.
comparableconstraintTypes usable with ==Only valid as a type parameter constraint.
Composite types
[N]TN × size of TFixed-length arrayThe length is part of the type: [3]int and [4]int differ.
[]T24-byte headerSlice: pointer, length, capacityA view into an array.
map[K]Vpointer-sizedHash tableThe zero value is nil, and writing to a nil map panics.
chan Tpointer-sizedChannel<-chan T receives only, chan<- T sends only.
*T8 bytes on 64-bitAddress of a TNo pointer arithmetic.
struct{...}sum of fields, plus paddingFields in declaration orderField order affects size, because of alignment.
func(...)pointer-sizedFunction valueFunctions are values and can be passed and stored.
interface{...}16 bytesMethod setHolds a type word and a value word.

Zero values

Declared asZero value
var b boolfalse
Any integer type0
Any float or complex type0 / 0.0
var s string"", the empty string

fmt verbs

VerbPrintsExample
%vDefault format for the valuefmt.Printf("%v", 42)42
%TGo-syntax type of the valuefmt.Printf("%T", 42)int
%dBase-10 integerfmt.Printf("%d", 255)255
%x / %XLower/upper-case hexadecimalfmt.Printf("%x", 255)ff
%fDecimal, no exponentfmt.Printf("%.2f", 3.14159)3.14
%tThe word true or falsefmt.Printf("%t", true)true
%sThe uninterpreted stringfmt.Printf("%s", "go")go
%qDouble-quoted, safely escapedfmt.Printf("%q", "hi\n")"hi\n"
%cThe character represented by a runefmt.Printf("%c", 65)A

Defined types vs. type aliases

DeclarationDistinct type?Can attach methods?Interchangeable without conversion?
type Name stringYesYesNo
type UserID = intNoNo (would attach to int itself, illegal outside its package)Yes

Conversion rules

From → ToAllowedNotes
Named type ↔ its underlying typeExplicit conversion, e.g. Celsius(f)Always allowed; never happens implicitly.
Numeric type ↔ numeric typeExplicit conversion, e.g. int64(x)May truncate or overflow silently; no runtime check.
string[]byteExplicit conversion, e.g. []byte(s)Copies the underlying bytes.
string[]runeExplicit conversion, e.g. []rune(s)Decodes/encodes UTF-8; length can change from byte count to rune count.
Concrete type → interface it implementsImplicit, no conversion syntaxHappens automatically on assignment if the method set matches.
Type alias ↔ its target typeImplicit, no conversion syntaxThey're the same type at compile time.

Behavior and edge cases

A bool holds true or false. It's the only type if, for, and &&/|| accept. Go has no numeric or pointer truthiness, so a nil check has to spell out != nil.

var active bool // false, the zero value
enabled := true

// Go has no truthy values. This does not compile:
// if 1 { }
if ptr != nil {
    fmt.Println("got a pointer")
}

Sized integers (int8 through int64, and their uint counterparts) pick an exact width. Converting between integer types requires explicit casting.

var count int       // 64-bit on most modern systems
var age uint8 = 30
var offset int64 = -1_000_000 // underscores group digits

hex := 0xFF
bin := 0b1010

// Conversion is always explicit
var x int32 = 42
var y int64 = int64(x)

len() on a string counts bytes, not characters. Multi-byte UTF-8 runes like é or take more than one byte, so byte (an alias for uint8) and rune (an alias for int32) exist to be explicit about which unit of text is meant.

word := "héllo"
fmt.Println(len(word)) // 6 bytes, not 5 characters

var b byte = 'A'   // byte is an alias for uint8
var r rune = '世'  // rune is an alias for int32

for i, ch := range word {
    fmt.Printf("%d: %c\n", i, ch) // indices skip a byte at multi-byte runes
}

The rune prints as int32 under %T, since rune is an alias not a type. %v prints the decimal value; %c further down prints it as a character.

Terminal
$ go run basic_types.go
Type: bool Value: true
Type: string Value: Hello, Go!
Type: int Value: 42
Type: float64 Value: 3.14159
Type: complex128 Value: (5+7i)
Type: int32 Value: A