Channels ▼
RSS

JVM Languages

Go Tutorial: Object Orientation and Go's Special Data Types


So, in Go (and C and C++ for that matter), the asterisk is overloaded to mean multiplication (when used between a pair of numbers or variables, as in x*y), pointer declaration (when preceding a type name, as in z *MyType), and pointer dereference (when preceding a pointer variable's name, as in *z). Note that Go's channels, maps, and slices are all created using the make() function, and make() always returns a reference to the value it created. References behave very much like pointers in that when they are passed to functions, any changes made to them inside the function also affect the original channel, map, or slice. However, references don't need to be dereferenced, so in most cases, there's no need to use asterisks with them. But if we want to modify a slice inside a function or method using append() (as opposed to simply changing one of its existing items), then we must either pass the slice by pointer, or return the slice (and set the original slice to the function or method's return value) because append() sometimes returns a different slice reference than the one it was passed.
The Stack type uses a slice for its representation, therefore Stack values can be used with functions that operate on a slice, such as append() and len(). Nonetheless, Stack values are values in their own right, distinct from their representation, so they must be passed by pointer if we want to modify them.

func (stack Stack) Top() (interface{}, error) { 
    if len(stack) == 0 {
        return nil, errors.New("can't Top() an empty stack") 
    }
    return stack[len(stack)-1], nil
}

The Stack.Top() method returns the item at the top of the stack (the item that was added last) and a nil error value; or a nil item and a non-nil error value, if the stack is empty. The stack receiver is passed by value because the stack wasn't modified.

The error type is an interface type that specifies a single method, the Error() string. In general, Go's library functions return an error as their last (or only) return value to indicate success (where error is nil) or failure. Here, we have made our Stack type work like a standard library type by creating a new error value using the errors package's errors.New() function.

Go uses nil for zero pointers (and for zero references); that is, for pointers that point to nothing and for references that refer to nothing. (Go's nil is effectively the same as NULL or 0 in C and C++, null in Java, and nil in Objective-C.) Such pointers should be used only in conditions or assignments; methods should not normally be called on them. Constructors are never called implicitly in Go. Instead, Go guarantees that when a value is created, it is always initialized to its zero value. For example, numbers are initialized to 0, strings to the empty string, pointers to nil, and the fields inside structs are similarly initialized. So there is no uninitialized data in Go, thus eliminating a major source of errors afflicting many other programming languages. If the zero value isn't suitable, we can write a construction function — and call it explicitly — as we do here to create a new error. It is also possible to prevent values of a type being created without using a constructor function.

If the stack is nonempty, we return its topmost value and a nil error value. Since Go uses zero-based indexing, the first element in a slice or array is at position 0 and the last element is at position len(sliceOrArray)-1.

There is no formality when returning more than one value from a function or method; we simply list the types we are returning after the function or method's name and ensure that we have at least one return statement that has a corresponding list of values.

func (stack *Stack) Pop() (interface{}, error) { 
    theStack := *stack 
    if len(theStack) == 0 {
        return nil, errors.New("can't Pop() an empty stack")
    } 
    x := theStack[len(theStack)-1] 
    *stack = theStack[:len(theStack)-1] 
    return x, nil
}

The Stack.Pop() method is used to remove and return the top (last added) item from the stack. Like the Stack.Top() method, it returns the item and a nil error, or if the stack is empty, a nil item and a non-nil error.

The method must have a receiver that is a pointer because it modifies the stack by removing the returned item. For syntactic convenience, rather than referring to *stack (the actual stack that the stack variable points to) throughout the method, we assign the actual stack to a local variable (theStack), and work with that variable instead. This is quite cheap, because *stack is pointing to a Stack, which uses a slice for its representation — so we are really assigning little more than a reference to a slice.

If the stack is empty, we return a suitable error. Otherwise, we retrieve the stack's top (last) item and store it in a local variable (x). Then we take a slice of the stack (which itself is a slice). The new slice has one less element than the original and is immediately set to be the value that the stack pointer points to. And at the end, we return the retrieved value and a nil error. We can reasonably expect any decent Go compiler to reuse the slice, simply reducing the slice's length by one, while leaving its capacity unchanged, rather than copying all the data to a new slice.

The item to return is retrieved using the [] index operator with a single index (➊); in this case, the index of the slice's last element. The new slice is obtained by using the [] slice operator with an index range (➋). An index range has the form first:end. If first is omitted — as here — 0 is assumed, and if end is omitted, the len() of the slice is assumed. The slice thus obtained has elements with indexes from and including the first up to and excluding the end. So in this case, by specifying the last index as one less than the length, we slice up to the last-but-one element, effectively removing the last element from the slice.

In this example, we used Stack receivers rather than pointers of type *Stack for those methods that don't modify the Stack. For custom types with lightweight representations (say, a few ints or strings), this is perfectly reasonable. But for heavyweight custom types, it is usually best to always use pointer receivers because a pointer is much cheaper to pass (typically a simple 32- or 64-bit value) than a large value, even for methods where the value isn't modified.

A subtle point to note regarding pointers and methods is that if we call a method on a value, and the method requires a pointer to the value it is called on, Go is smart enough to pass the value's address rather than a copy of the value (providing the value is addressable). Correspondingly, if we call a method on a pointer to a value, and the method requires a value, Go is smart enough to dereference the pointer and give the method the pointed-to value. (This is why Go does not have or need the -> indirection operator used by C and C++.)\As this example illustrates, creating custom types in Go is generally straightforward, and doesn't involve the cumbersome formalities that many other languages demand.

Files, Maps, and Closures

To have any practical use, a programming language must provide some means of reading and writing external data. Now that we've had a glimpse of Go's versatile and powerful print functions from its fmt package, we will look at Go's basic file-handling facilities. We will also look at some more advanced features such as Go's treatment of functions and methods as first-class values, which makes it possible to pass them as parameters. And in addition, we will make use of Go's map type (also known as a data dictionary or hash).

By the mid-twentieth century, American English surpassed British English as the most widely used form of English. In this section's example, we will review a program that reads a text file and writes out a copy of the file into a new file with any words using British spellings replaced with their U.S. counterparts. (This doesn't help with differences in semantics or idioms, of course.) The program is in the file americanise/americanise.go, and we will review it top-down, starting with its imports, then its main() function, then the functions that main() calls, and so on.

import ( 
    "bufio"
    "fmt" 
    "io" 
    "io/ioutil" 
    "log" 
    "os" 
    "path/filepath" 
    "regexp" 
    "strings"
)

All the americanise program's imports are from Go's standard library. Packages can be nested inside one another without formality, as the io package's ioutil package and the path package's filepath package illustrate.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video