Data Types In GO:

Data Types In GO:

A deeper dive..

Declarations:

THE ZERO VALUE:

Like most modern languages GO assigns a default zero value to any variable declared but not assigned a value.

LITERALS:

Refers to writing out a number, character, or string in GO.
There are different types of literals:
a. Integer literals: These are sequences of numbers. There can be any base eg. 0b (for binary, 0o(for octal), 0x(for hexadecimal). b,o,x can be either uppercase or lowercase.
To make it easier to read longer integer literals GO allows us to put underscores in the middle of the literals eg. 1_234, 1_2_3_4. Underscores are not permitted at the beginning, end, or side-by-side with other underscores.

b. Floating-Point literals: these have decimal points to indicate the fractional part of the value, we can also use an exponent with 'e' and can either have a positive or negative value eg. 1.632e23. In the case using base 16, use the prefix 0x and 'p' for the exponent. Just like integer literals, we can use underscores here.

c. Rune literals: represents characters in single quotes(' '). In GO single and double quotes are not interchangeable. Examples include:

  • newline ('\n'), tab ('\t'), single quote (' ' '), double quote (' " '), and backslash ('\').

  • single Unicode characters ('a'),8-bit octal numbers ('\141'), 8-bit hexadecimal numbers ('\x61'), 16-bit hexadecimal numbers ('\u0061'), or 32-bit Unicode numbers ('\U00000061').

There are two different ways to indicate string literals. Most of the time, you should use double quotes to create an interpreted string literal (e.g., type "Greetings and Salutations"). These contain zero or more rune literals, in any of the forms allowed. The only characters that cannot appear are unescaped backslashes, unescaped newlines, and unescaped double quotes.

If you use an interpreted string literal and want your greetings on a different line from your salutations and you want “Salutations” to appear in quotes, you need to type "Greetings and\n" Salutations" ".

If you need to include backslashes, double quotes, or newlines in your string, use a raw string literal. These are delimited with backquotes (`) and can contain any literal character except a backquote. When using a raw string literal, we write our multiline greeting like so:

`Greetings and
"Salutations"`

Note: Literals in Go are untyped; they can interact with any variable that’s compatible with the literal. But you can’t assign a literal string to a variable with a numeric type or a literal number to a string variable, nor can you assign a float literal to an int. These are all flagged by the compiler as errors.

There are size limitations too; while you can write numeric literals that are larger than any integer can hold, it is a compile-time error to try to assign a literal whose value overflows the specified variable, such as trying to assign the literal 1000 to a variable of type byte.

BOOLEANS:

The bool type can have either true or false. Zero-value for bool is false.

var flag bool // set to false by default
var isGreat = true

NUMERIC TYPES:

Go has a large number of numeric types: 12 different types(and a few special names):

A) INTEGER TYPES:

Go provides both signed and unsigned integers in a variety of sizes, from 1 to 4 bytes. Zero-value for all integer types is 0.

The special integer types Go does have some special names for integer types.

A byte is an alias for uint8; it is legal to assign, compare, or perform mathematical operations between a byte and a uint8. However, you rarely see uint8 used in Go code; just call it a byte.

The second special name is int. On a 32-bit CPU, int is a 32-bit signed integer like an int32. On most 64-bit CPUs, int is a 64-bit signed integer, just like an int64. Because int isn’t consistent from platform to platform, it is a compile-time error to assign, compare, or perform mathematical operations between an int and an int32 or int64 without a type conversion. Integer literals default to being of int type.

The third special name is uint. It follows the same rules as int, only it is unsigned (the values are always 0 or positive).

Choosing which Integer to use?

• If you are working with a binary file format or network protocol that has an inte‐ ger of a specific size or sign, use the corresponding integer type.

• If you are writing a library function that should work with any integer type, write a pair of functions, one with int64 for the parameters and variables and the other with uint64.

• In all other cases just use int.

B) FLOATING POINT TYPES:

There are 2 floating-point types. Zero-value for the floating-point type is again 0.

Go uses the IEEE 754 specification, giving a large range and limited precision. Picking which floating point type to use is straightforward: unless you have to be compatible with an existing format, use float64. Floating point literals have a default type of float64, so always using float64 is the simplest option.

  • You can use all the standard mathematical and comparison operators with floats, except %.

  • Dividing a nonzero floating point variable by 0 returns +Inf or -Inf (positive or negative infinity), depending on the sign of the number.

  • Dividing a floating point variable set to 0 by 0 returns NaN (Not a Number).

STRING TYPE:

Go includes strings as a built-in type. The zero value for a string is the empty string. Go supports Unicode; you can put any Unicode character into a string. Like integers and floats, strings are compared for equality using ==, the difference with !=, or ordering with >, >=, <, or <=. They are concatenated by using the + operator.

Strings in Go are immutable; you can reassign the value of a string variable, but you cannot change the value of the string that is assigned to it.

Go also has a type that represents a single code point. The rune type is an alias for the int32 type, just like the byte is an alias for uint8. A rune literal’s default type is a rune, and a string literal’s default type is a string.

If you are referring to a character, use the rune type, not the int32 type. They might be the same to the compiler, but you want to use the type that clarifies the intent of your code.

EXPLICIT TYPE CONVERSION:

Most languages that have multiple numeric types automatically convert from one to another when needed. This is called automatic type promotion, and while it seems very convenient, it turns out that the rules to properly convert one type to another can get complicated and produce unexpected results.

As a language that values clarity of intent and readability, Go doesn’t allow automatic type promotion between variables. You must use a type conversion when variable types do not match. Even different-sized integers and floats must be converted to the same type to interact. This makes it clear exactly what type you want without having to memorize any type of conversion rules.

// Examples
var x int = 10 
var y float64 = 30.2
var z float64 = float64(x) + y 
var d int = x + int(y)

Go doesn’t allow truthiness. No other type can be converted to bool, implicitly or explicitly. If you want to convert from another data type to a boolean, you must use one of the comparison operators (==, !=, >, <, <=, or >=).

For example, to check if variable x is equal to 0, the code would be x == 0. If you want to check if string s is empty, use s == " " .

'Var ' Versus ':= ' :

Go has a lot of ways to declare variables. Each declaration style communicates something about how the variable is used.

The most verbose way to declare a variable in Go uses the var keyword, an explicit type, and an assignment. It looks like this:

var x int = 10

If the type on the righthand side of the = is the expected type of your variable, you can leave off the type from the left side of the =. Since the default type of an integer literal is int, the following declares x to be a variable of type int:

var x = 10

Conversely, if you want to declare a variable and assign it the zero value, you can keep the type and drop the = on the righthand side:

var x int

You can declare multiple variables at once with var, and they can be of the same type:

var x, y int = 10, 20

You can declare multiple variables at once with var, and they can be of the same type:

var x, y int

or of different types:

var x, y = 10, "hello"

There’s one more way to use var. If you are declaring multiple variables at once, you can wrap them in a declaration list:

var ( 
x int
y = 20 
z int = 30 
d, e = 40, "hello"
f, g string 
)

Go also supports a short declaration format. When you are within a function, you can use the := operator to replace a var declaration that uses type inference. The following two statements do exactly the same thing: they declare x to be an int with the value of 10:

var x=10
x:=10

Like var, you can declare multiple variables at once using :=. These two lines both assign 10 to x and “hello” to y:

var x, y = 10, "hello"
x, y := 10, "hello"

The := operator can do one trick that you cannot do with var: it allows you to assign values to existing variables, too. As long as there is one new variable on the lefthand side of the :=, then any of the other variables can already exist:

x := 10 
x, y := 30, "hello"

There is one limitation on :=. If you are declaring a variable at the package level, you must use var because := is not legal outside of functions.

How do you know which style to use?
As always, choose what makes your intent clearest. The most common declaration style within functions is :=.

Outside of a function, use declaration lists on the rare occasions when you are declaring multiple package-level variables.

There are some situations within functions where you should avoid := :

• When initializing a variable to its zero value, use var x int. This makes it clear that the zero value is intended.

• When assigning an untyped constant or a literal to a variable and the default type for the constant or literal isn’t the type you want for the variable, use the long var form with the type specified. While it is legal to use a type conversion to specify the type of the value and use := to write x := byte(20), it is idiomatic to write var x byte = 20.

• Because := allows you to assign to both new and existing variables, it sometimes creates new variables when you think you are reusing existing ones. In those situations, explicitly declare all of your new variables with var to make it clear which variables are new, and then use the assignment operator (=) to assign values to both new and old variables.

While var and := allow you to declare multiple variables on the same line, only use this style when assigning multiple values returned from a function.

\*Avoid declaring variables outside of functions because they complicate data flow analysis.*

Using Const:

When developers learn a new programming language, they try to map familiar concepts. Many languages have a way to declare a value is immutable. In Go, this is done with the const keyword.

const x int64 = 10 
const ( 
idKey = "id" 
nameKey = "name" 
) 
const z = 20 * 10 
func main() {
 const y = "hello" 
fmt.Println(x) 
fmt.Println(y) 
x = x + 1 
y = "bye" 
fmt.Println(x) 
fmt.Println(y)
 }

If you try to run this code, compilations fails with the following error messages: ./const.go:20:4: cannot assign to x
./const.go:21:4: cannot assign to y

Constants in Go are a way to give names to literals. They can only hold values that the compiler can figure out at compile time. This means that they can be assigned:
• Numeric literals
• true and false
• Strings
• Runes
• The built-in functions are complex, real, imag, len, and cap
• Expressions that consist of operators and the preceding values

Go doesn’t provide a way to specify that a value calculated at runtime is immutable. There are no immutable arrays, slices, maps, or structs, and there’s no way to declare that a field in a struct is immutable. This is less limiting than it sounds. Within a function, it is clear if a variable is being modified, so immutability is less important. We’ll see how Go prevents modifications to variables that are passed as parameters to functions.

Typed and Untyped Constants:

Constants can be typed or untyped.
An untyped constant works exactly like a literal; it has no type of its own, but does have a default type that is used when no other type can be inferred.
A typed constant can only be directly assigned to a variable of that type.

Whether or not to make a constant typed depends on why the constant was declared. If you are giving a name to a mathematical constant that could be used with multiple numeric types, then keep the constant untyped.
In general, leaving a constant untyped gives you more flexibility. There are situations where you want a constant to enforce a type.
Here’s what an untyped constant declaration looks like:
const x = 10
All of the following assignments are legal:
var y int = x
var z float64 = x
var d byte = x
Here’s what a typed constant declaration looks like:
const typedX int = 10
This constant can only be assigned directly to an int. Assigning it to any other type produces a compile-time error like this:
cannot use typedX (type int) as type float64 in assignment

Unused Variables:

Go programs need to be formatted in a specific way with go fmt to make it easier to write code-manipulation tools and to provide coding standards. Another Go requirement is that every declared local variable must be read. It is a compile-time error to declare a local variable and to not read its value. The compiler’s unused variable check is not exhaustive. As long as a variable is read once, the compiler won’t complain, even if there are writes to the variable that are never read. The following is a valid Go program that you can run on The Go Playground:

func main() { 
x := 10
x = 20 
fmt.Println(x) 
x = 30
}

While the compiler and go vet do not catch the unused assignments of 10 and 30 to x, golangci-lint detects them:
$ golangci-lint run unused.go:6:2: ineffectual assignment to x (ineffassign)
x := 10
^
unused.go:9:2: ineffectual assignment to x (ineffassign)
x = 30
^

\* The Go compiler won’t stop you from creating unread package-level variables. This is one more reason why you should avoid creating package-level variables.*
Perhaps surprisingly, the Go compiler allows you to create unread constants with const. This is because constants in Go are calculated at compile time and cannot have any side effects. This makes them easy to eliminate: if a constant isn’t used, it is simply not included in the compiled binary.

Naming Variables and Constants:

There is a difference between Go’s rules for naming variables and the patterns that Go developers, follow when naming their variables and constants. Like most languages, Go requires identifier names to start with a letter or underscore, and the name can contain numbers, underscores, and letters. Go’s definition of “letter” and “number” is a bit broader than in many languages. Any Unicode character that is considered a letter or digit is allowed. This makes all of the variable definitions shown in Examples 2-4 perfectly valid Go.

_0 := 0
_0 _𝟙 := 20
π := 3 
a := "hello" // Unicode U+FF41 
fmt.Println(_0) 
fmt.Println(_𝟙) 
fmt.Println(π) 
fmt.Println(a)

While this code works, do not name your variables like this. These names are considered non-idiomatic because they break the fundamental rule of making sure that your code communicates what it is doing. These names are confusing or difficult to type on many keyboards. Look-alike Unicode code points are the most insidious, because even if they appear to be the same character, they represent entirely different variables.

// Example 2-5. Using look-alike code points for variable names 
func main() { 
a := "hello" // Unicode U+FF41 
a := "goodbye" // standard lowercase a (Unicode U+0061) 
fmt.Println(a) 
fmt.Println(a) 
}

When you run this program, you get:

hello
goodbye
Even though underscore is a valid character in a variable name, it is rarely used, because idiomatic Go doesn’t use snake case (names like index_counter or num‐ ber_tries). Instead, Go uses camel case (names like indexCounter or numberTries) when an identifier name consists of multiple words.

In many languages, constants are always written in all uppercase letters, with words separated by underscores (names like INDEX_COUNTER or NUMBER_TRIES). Go does not follow this pattern. This is because Go uses the case of the first letter in the name of a package-level declaration to determine if the item is accessible outside the package.

Within a function, favor short variable names. The smaller the scope for a variable, the shorter the name that’s used for it. It is very common in Go to see single-letter variable names. For example, the names k and v (short for key and value) are used as the variable names in a for-range loop. If you are using a standard for loop, i and j are common names for the index variable.

Some languages with weaker type systems encourage developers to include the expected type of the variable in the variable’s name. Since Go is strongly typed, you don’t need to do this to keep track of the underlying type. However, there are still conventions around variable types and single-letter names. People will use the first letter of a type as the variable name (for example, i for integers, f for floats, and b for booleans). When you define your own types, similar patterns apply.

These short names serve two purposes. The first is that they eliminate repetitive typing, keeping your code shorter. Second, they serve as a check on how complicated your code is. If you find it hard to keep track of your short-named variables, your block of code is likely doing too much.

When naming variables and constants in the package block, use more descriptive names. The type should still be excluded from the name, but since the scope is wider, you need a more complete name to make it clear what the value represents.