Lesson 10. Converting between types

After reading lesson 10, you’ll be able to

  • Convert between numeric, string, and Boolean types

Previous lessons covered Booleans, strings, and a dozen different numeric types. If you have variables of different types, you must convert the values to the same type before they can be used together.

Consider this

Say you’re at the grocery store with a shopping list from your spouse. The first item is milk, but should you get cow’s milk, almond, or soy? Should it be organic, skim, 1%, 2%, whole, evaporated, or condensed? How many gallons? Do you call your spouse to ask or just pick something?

Your spouse may get annoyed if you keep calling to ask for each detail. Iceberg or romaine lettuce? Russet or red potatoes? Oh, and was that 5 lbs. or 10? On the other hand, if you “think for yourself” and return with chocolate milk and french fries, that may not go over so well.

If your spouse is a programmer and you’re a compiler in this scenario, what do you think Go’s approach would be?

10.1. Types don’t mix

A variable’s type establishes the behavior that’s appropriate for it. Numbers can be added, strings can be joined. To join two strings together, use the plus operator:

countdown := "Launch in T minus " + "10 seconds."

If you try to join a number to a string, the Go compiler will report an error:

countdown := "Launch in T minus " + 10 + " seconds."        1

  • 1 Invalid operation: mismatched types string and int
Mixing types in other languages

When presented with two or more different types, some programming languages make a best effort to guess the programmer’s intentions. Both JavaScript and PHP can subtract 1 from the string "10":

"10" - 1         1

  • 1 9 in JavaScript and PHP

The Go compiler rejects "10" - 1 with a mismatched types error. In Go, you first need to convert "10" to an integer. The Atoi function in the strconv package will do the conversion, but it will return an error if the string doesn’t contain a valid number. By the time you handle errors, the Go version is four lines long, which isn’t exactly convenient.

That said, if "10" is user input or came from an external source, the JavaScript and PHP versions should check whether it’s a valid number too.

In languages that coerce types, the code’s behavior is less predictable to anyone who hasn’t memorized a myriad of implicit behaviors. The plus operator (+) in both Java and JavaScript coerces numbers to strings to be joined, whereas PHP coerces the values to numbers and does the math:

"10" + 2         1

  • 1 “102” in JavaScript or Java, 12 in PHP

Once again, Go would report a mismatched types error.

Another example of mismatched types occurs when attempting a calculation with a mix of integer and floating-point types. Real numbers like 365.2425 are represented with a floating-point type, and Go infers that whole numbers are integers:

 age := 41                                                             1
 marsDays := 687                                                       1
 earthDays := 365.2425                                                 2
 fmt.Println("I am", age*earthDays/marsDays, "years old on Mars.")     3

  • 1 age and marsDays are integers.
  • 2 earthDays is a floating point type.
  • 3 Invalid operation: mismatched types

If all three variables were integers, the calculation would succeed, but then earthDays would need to be 365 instead of the more accurate 365.2425. Alternatively, the calculation would succeed if age and marsDays were floating-point types (41.0 and 687.0 respectively). Go doesn’t make assumptions about which you’d prefer, but you can explicitly convert between types, which is covered in the next section.

Quick check 10.1

Q1:

What is "10" - 1 in Go?

QC 10.1 answer

1:

A compiler error: invalid operation: “10” - 1 (mismatched types string and int)

 

10.2. Numeric type conversions

Type conversion is straightforward. If you need the integer age to be a floating-point type for a calculation, wrap the variable with the new type:

age := 41
marsAge := float64(age)

Variables of different types don’t mix, but with type conversion, the calculation in the following listing works.

Listing 10.1. Mars age: mars-age.go
age := 41
marsAge := float64(age)

marsDays := 687.0
earthDays := 365.2425
marsAge = marsAge * earthDays / marsDays
fmt.Println("I am", marsAge, "years old on Mars.")          1

  • 1 Prints I am 21.797587336244543 years old on Mars.

You can convert from a floating-point type to an integer as well, though the digits after the decimal point will be truncated without any rounding:

fmt.Println(int(earthDays))            1

  • 1 Prints 365

Type conversions are required between unsigned and signed integer types, and between types of different sizes. It’s always safe to convert to a type with a larger range, such as from an int8 to an int32. Other integer conversions come with some risks. A uint32 could contain a value of 4 billion, but an int32 only supports numbers to just over 2 billion. Likewise, an int may contain a negative number, but a uint can’t.

There’s a reason why Go requires type conversions to be explicitly stated in the code. Every time you use a type conversion, consider the possible consequences.

Quick check 10.2

1

What code would convert the variable red to an unsigned 8-bit integer?

2

What is the result of the comparison age > marsAge?

QC 10.2 answer

1

The type conversion would be uint8(red).

2

Mismatched types int and float64

 

10.3. Convert types with caution

In 1996, the unmanned Arianne 5 rocket veered off its flight path, broke up, and exploded just 40 seconds after launch. The reported cause was a type conversion error from a float64 to an int16 with a value that exceeded 32,767—the maximum value an int16 can hold. The unhandled failure left the flight control system without orientation data, causing it to veer off course, break apart, and ultimately self-destruct.

We haven’t seen the Arianne 5 code, nor are we rocket scientists, but let’s look at how Go handles the same type conversion. If the value is in range, as in the following listing, no problem.

Listing 10.2. Ariane type conversion: ariane.go
var bh float64 = 32767
var h = int16(bh)             1
fmt.Println(h)

  • 1 To-do: add rocket science

If the value of bh is 32,768, which is too big for an int16, the result is what we’ve come to expect of integers in Go: it wraps around, becoming the lowest possible number for an int16, –32768.

The Ada language used for the Arianne 5 behaves differently. The type conversion from float64 to int16 with an out-of-range value caused a software exception. According to the report, this particular calculation was only meaningful prior to liftoff, so Go’s approach may have been better in this instance, but usually it’s best to avoid incorrect data.

To detect whether converting a type to int16 will result in an invalid value, the math package provides min/max constants:

if bh < math.MinInt16 || bh > math.MaxInt16 {
    // handle out of range value
}
Note

These min/max constants are untyped, allowing the comparison of bh, a floating-point value, to MaxInt16. Lesson 8 talks more about untyped constants.

Quick check 10.3

Q1:

What code will determine if the variable v is within the range of an 8-bit unsigned integer?

QC 10.3 answer

1:

v := 42
if v >= 0 && v <= math.MaxUint8 {
    v8 := uint8(v)
    fmt.Println("converted:", v8)       1
}

  • 1 Prints converted: 42

 

10.4. String conversions

To convert a rune or byte to a string, you can use the same type conversion syntax as numeric conversions, as shown in the next listing. This gives the same result using the %c format verb introduced in lesson 9 to display runes and bytes as characters.

Listing 10.3. Converting rune to string: rune-convert.go
var pi rune = 960
var alpha rune = 940
var omega rune = 969
var bang byte = 33

fmt.Print(string(pi), string(alpha), string(omega), string(bang))      1

  • 1 Prints πάω!

Converting a numeric code point to a string works the same with any integer type. After all, rune and byte are just aliases for int32 and uint8.

To convert digits to a string, each digit must be converted to a code point, starting at 48 for the 0 character, through 57 for the 9 character. Thankfully, the Itoa function in the strconv (string conversion) package does this for you, as shown in the next listing.

Listing 10.4. Integer to ASCII: itoa.go
countdown := 10

str := "Launch in T minus " + strconv.Itoa(countdown) + " seconds."
fmt.Println(str)                                                     1

  • 1 Prints Launch in T minus 10 seconds.
Note

Itoa is short for integer to ASCII. Unicode is a superset of the old ASCII standard. The first 128 code points are the same, which includes digits (used here), English letters, and common punctuation.

Another way to convert a number to a string is to use Sprintf, a cousin of Printf that returns a string rather than displaying it:

countdown := 9
str := fmt.Sprintf("Launch in T minus %v seconds.", countdown)
fmt.Println(str)                                                   1

  • 1 Prints Launch in T minus 9 seconds.

To go the other way, the strconv package provides the Atoi function (ASCII to integer). Because a string may contain gibberish or a number that’s too big, the Atoi function may return an error:

countdown, err := strconv.Atoi("10")
if err != nil {
    // oh no, something went wrong
}
fmt.Println(countdown)         1

  • 1 Prints 10

A nil value for err indicates that no error occurred and everything is A-OK. Lesson 28 navigates the perilous topic of errors.

Quick check 10.4

Q1:

Name two functions that can convert an integer to a string.

QC 10.4 answer

1:

Both Itoa and Sprintf will convert a whole number to a string.

 

Types are static

In Go, once a variable is declared, it has a type and the type cannot be changed. This is known as static typing, which is easier for the compiler to optimize, so your programs run fast. But attempting to use a variable with a value of a different type will cause the Go compiler to report an error:

var countdown = 10
countdown = 0.5                                     1
countdown = fmt.Sprintf("%v seconds", countdown)    1

  • 1 Error: countdown can only store integers.

Languages such as JavaScript, Python, and Ruby use dynamic typing instead of static typing. In those languages, each value has an associated type, and variables can hold values of any type. They would allow the type of countdown to change as the program executes.

Go does have an escape hatch for situations where the type is uncertain. For example, the Println function will accept both strings and numeric types. Lesson 12 explores the Println function in more detail.

10.5. Converting Boolean values

The Print family of functions displays the Boolean values true and false as text. As such, the next listing uses the Sprintf function to convert the Boolean variable launch to text. If you want to convert to numeric values or different text, a humble if statement works best.

Listing 10.5. Converting a Boolean to a string: launch.go
launch := false

launchText := fmt.Sprintf("%v", launch)
fmt.Println("Ready for launch:", launchText)          1

var yesNo string
if launch {
    yesNo = "yes"
} else {
    yesNo = "no"
}
fmt.Println("Ready for launch:", yesNo)               2

  • 1 Prints Ready for launch: false
  • 2 Prints Ready for launch: no

The inverse conversion requires less code because you can assign the result of a condition directly to a variable, as in the following listing.

Listing 10.6. Converting a string to a Boolean: tobool.go
yesNo := "no"

launch := (yesNo == "yes")
fmt.Println("Ready for launch:", launch)      1

  • 1 Prints Ready for launch: false

The Go compiler will report an error if you attempt to convert a Boolean with string(false), int(false), or similar, and likewise for bool(1) or bool("yes").

Note

In programming languages without a dedicated bool type, the values 1 and 0 often stand in for true and false, respectively. Booleans in Go don’t have a numeric equivalent.

Quick check 10.5

Q1:

How would you convert a Boolean to an integer, with 1 for true and 0 for false?

QC 10.5 answer

1:

With a humble if statement:

launch := true

var oneZero int
if launch {
    oneZero = 1
} else {
    oneZero = 0
}
fmt.Println("Ready for launch:", oneZero)      1

  • 1 Prints Ready for launch: 1

 

Summary

  • Conversion between types is explicit to avoid ambiguity.
  • The strconv package provides functions for converting strings to and from other types.

Let’s see if you got this...

Experiment: input.go

Write a program that converts strings to Booleans:

  • The strings “true”, “yes”, or “1” are true.
  • The strings “false”, “no”, or “0” are false.
  • Display an error message for any other values.
Tip

The switch statement accepts multiple values per case, as covered in lesson 3.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset