Booleans and Type Conversion

A bool is only true or false, a condition must be a bool, and every change of type is written out as T(v).

SyntaxT(v) — explicit conversion; a condition must already be a bool, never a number or a string

A bool is a value with exactly two possibilities: true or false. If you declare one without a value it starts as false. A comparison such as age >= 18 produces a bool, and you may store it in a variable, print it, or pass it around like any other value.

No truthiness

In some languages the number 0 or an empty string counts as false in a condition. Go has nothing of the kind. The thing you test must already be a bool. if count does not compile when count is a number; you write if count == 0. There is a shorthand worth knowing: when ok is already a bool, if ok and if ok == true mean the same thing, and !ok means the same as ok == false. The short forms are the usual ones.

T(v) is the only way to change type

Go never changes a value's type behind your back. You write the conversion: the type name, then the value in brackets, as int(f) or float64(n). Conversions work between numeric types, between a string and []byte or []rune, and between types built on the same underlying type. A bool converts to nothing at all, so to turn one into 0 or 1 you write the two cases out.

What trips people up

string(65) does not give "65". The specification says converting a number to a string yields the UTF-8 form of that code point, so the answer is "A". Spelling a number out is a different job, done by strconv.Itoa or by fmt.Sprintf.

Example

Example · go
package main

import (
	"fmt"
	"strconv"
)

func main() {
	// A bool holds true or false. Declared with no value, it is false.
	var ready bool
	fmt.Println(ready)

	// A condition must be a bool. "if count" does not compile.
	count := 0
	if count == 0 {
		fmt.Println("empty")
	}

	// T(v) converts: the type name, then the value in brackets.
	var f float64 = 3.9
	fmt.Println(int(f))

	// Converting reads the number as a code point. Itoa spells it out.
	fmt.Printf("%q %q\n", string(rune(65)), strconv.Itoa(65))

	// A bool converts to no other type. Write the two cases out.
	n := 0
	if ready {
		n = 1
	}
	fmt.Println(n)
}

// Output:
// false
// empty
// 3
// "A" "65"
// 0

When to use it

  • Test whether a counter is zero without relying on a number counting as false.
  • Name a comparison once as a bool instead of repeating it in three conditions.
  • Build a log line out of an int and a float64 with fmt.Sprintf.
  • Check a value fits before narrowing an int64 into a uint8 field.

More examples

Narrowing a number loses what does not fit

A conversion to a smaller type keeps only the low bits, and converting back does not recover the rest. There is no error and no warning, at compile time or at run time. Converting a float to an integer always cuts towards zero rather than rounding, in both directions. Check the range yourself before narrowing anything that came from outside your program.

Example · go
package main

import "fmt"

func main() {
	// Narrowing keeps only the low bits. No error, no warning.
	var big int32 = 300
	var small int8 = int8(big)
	fmt.Println(big, small)

	// Converting back does not recover what was dropped.
	fmt.Println(int32(small))

	// Floats are cut towards zero, never rounded. These must be variables:
	// int(2.9) on a constant is a compile error, not a truncation.
	x := 2.9
	y := -2.9
	fmt.Println(int(x), int(y))
}

// Output:
// 300 44
// 44
// 2 -2

Storing and testing a bool

A comparison is a value, so you can name it once and reuse it, which usually reads better than repeating the comparison. The example puts the short and the long test side by side so you can recognise both in other people's code. %t is the Printf verb for a bool, and %v prints anything, so either works here.

Example · go
package main

import "fmt"

func main() {
	age := 20

	// A comparison produces a bool you can name, store and print.
	adult := age >= 18
	fmt.Println(adult)

	// "if adult" is shorthand for "if adult == true". Same test.
	if adult {
		fmt.Println("can vote")
	}
	if adult == true {
		fmt.Println("same test, longer")
	}

	// ! flips a bool, so !adult means adult == false.
	fmt.Println(!adult, adult == false)

	// %t prints a bool. %v prints anything.
	fmt.Printf("%t %v\n", adult, adult)
}

// Output:
// true
// can vote
// same test, longer
// false false
// true true

Turning numbers into text on purpose

Converting and formatting are different operations and it is worth seeing them next to each other. strconv.Itoa, FormatBool and FormatFloat are functions that produce text spelling the value out. string(rune(42)) is a conversion and gives the character numbered 42. In everyday code fmt.Printf and fmt.Sprintf do this work, because they handle several values and some layout at once.

Example · go
package main

import (
	"fmt"
	"strconv"
)

func main() {
	// These are functions that spell a value out as text.
	fmt.Println(strconv.Itoa(42))
	fmt.Println(strconv.FormatBool(true))
	// 'f' means plain decimal, 2 means two decimal places, 64 means float64.
	fmt.Println(strconv.FormatFloat(3.14159, 'f', 2, 64))

	// This is a conversion, and it means something else: character 42.
	fmt.Printf("%q\n", string(rune(42)))

	// Printf is the usual way to build text out of several values.
	fmt.Printf("%d items at %.2f each\n", 3, 1.5)
}

// Output:
// 42
// true
// 3.14
// "*"
// 3 items at 1.50 each

Discussion

  • Be the first to comment on this lesson.