Skip to main content
Thoughts from David Cornelius


As mentioned in my last entry, I've recently become curious about the Go programming language, so I took a course to learn the language, partially to support add-ons for Hugo and partially to compare with other languages I've used--most notably, Delphi. What I found was a simple but powerful language, thoughtfully designed, that promotes good coding style and breaks some established patterns of thought I've had with object-oriented programming.

One of the big advantages of Go over other popular languages is that, like C++ and Delphi, it compiles to an executable binary instead of being scripted like PHP, Python, or Ruby. This is a proudly touted feature that also helps promote Hugo.

The syntax, at first glance, appears to mimic C or JavaScript but there are a couple of surprising similarities with Delphi. Furthermore, assumptions made by the compiler that encourage you to simplify your code, make it resemble Python in a way.

In this blog, I'll give a brief overview of what I learned about the Go language from the perspective of a programmer who's used mostly Delphi (Pascal) but with a sprinkling of other languages (C, C++, C#, VB, and PHP) throughout his career.

First, a disclaimer: I'm very new to the Go programming language and some of what I describe may not be completely accurate--it's based on what I understood as I went through the introductory course I just completed and the simple programs I wrote. There are likely exceptions to what I describe or the generalities expressed may not be as prevalent in the industry as I suspect. Also, my concentration was simply the language and writing simple console apps--I have not yet been exposed to the wide array of packages in the Go ecosystem or done any Windows or cross-platform exploration with the language.


A file of Go code (a module) starts with the keyword package and a name. All .go files in the same directory typically share the same package name. To use functions and types from an external package of library code, you import it (like the uses clause in Delphi). The .go files in the same folder do not have to be explicitly imported--they're automatically included in the package.

Go is a case-sensitive language. This is especially important when naming functions and identifiers at the package level. In Delphi, things that need to be accessed outside of a unit are placed in the interface section and the hidden details are in the implementation section. In Go, you simply capitalize identifiers (proper-cased, not all caps) to make them visible outside of the package; because of this, package-level identifiers are lower-case (or at least start with a lower-case letter) unless you need them available from elsewhere.


Functions start with the keyword func followed by the function name and parenthesis with optional parameters inside the parenthesis followed by an optional return type and then the block of code that makes up the function. Blocks are delineated in Delphi with begin and end but use curly braces in Go--just like C# and Java and several other languages. When a Go program starts, it always looks for and runs the main function:

func main() {


If the function does not return a value, it's return type is simply left off as in the main function depicted above; this is like a void function in C and the procedure type in Delphi. Unlike other any other language that I know, Go functions can have multiple return values. A typical use for this is to return an error along with the result; for example, the Atoi() function from the strconv standard Go package, converts a string to an integer, returning both the integer (if successful) and the possible error:

  n, err := strconv.Atoi(s)

You can then test err and if it's nil, proceed to use the successfully converted integer, n. In my opinion, this is much nicer than wrapping this in a try-except block in Delphi or returning the error in a var parameter. Speaking of parameters, there are no var parameters in Go; all parameters in Go are passed by reference; however, you can also pass pointers and the functions can modify the value at which they point.

A function with two parameters and one return value could be declared like this:

func DoSomething(n int, s string) bool {

To declare two return values, wrap them in parenthesis:

func ConvertValue(v string) (n int, ok error) {

By the way, error is a built-in "interface" type and has an Error() string function. I'll talk about interfaces below.


You may have noticed the sample Go code here does not have any semicolons at the ends of the lines. While the formal Go grammer does require semicolons to terminate statements, an interesting aspect of the Go lexer is that it automatically inserts them as it scans the code based on the context of the line. You can have them in your code but they're not necessary in most cases--and therefore, discouraged.


If you saw the variable assignment above with the := and caught your breath exclaiming, "That's like Delphi!" then I'll pause ... and let you revel in this short moment because the := has a very special use case--and it's not used as often as you might hope.

Go, like Delphi, is a type-strict language; each variable has a specific type established when it is declared. There are two ways to declare variables. The first is somewhat similar to Delphi because it uses the var keyword (but doesn't use a colon between identifiers and their type); there isn't a var declaration section, each variable is defined on its own line whenever you need one:

var n int
var s string

Later, you can assign values to them with a single = sign:

n = 1
s = "hello"

The second way to declare a variable is by initializing it with a value from which it gleans the type and declares it implicitly. This is the special case (and the only time) where the := is used in Go:

n := 1
s := "hello"

In this case, there's no need for the var keyword or explicitly declaring the variable types as the type is inferred with the value assignment; in other words, n is implicitly declared as an int and s as a string. The := can only be used once on an identifier within a scope; after declaring and initializing a variable, further assignments can only be done with the single = operator.

One other note is that declaring a variable without an immediate assignment of your value always initializes the variable to the "zero-value" for its type; numeric variables are set to zero, string variables to an empty string, pointers to nil, and Booleans to false; this is also true with arrays where each element of the array is initialized to the zero-value for its element type. This is nice to count on rather than be subject to the fate of whatever value happens to be in the memory location for a new variable--I've seen many hard-to-find bugs because of uninitialized variables (which, of course, a good programmer would never have!).


Speaking of variable types, here are the basic variable types in Go:

  • bool - (true/false)
  • byte - (alias for uint8)
  • rune - (alias for int32)
  • int - (many variations--see below)
  • float32/float64
  • complex64/complex128
  • string
  • struct
  • interface

Like Delphi, there are many variations of integers depending on the size of number you need to deal with and the platform you're on: int8 (-128..127), uint8 (0..255), int16, uint16, int32, uint32, int64, and uint64; the standard int type is either int32 or int64 depending on the platform.

Strings are immutable and 0-indexed. For example, in s := "Hello", the first character is referenced with s[0] and the last one with s[len(s)-1].

Each of the basic types can be used in a arrays, slices, or maps. You've likely heard of arrays but what are slices or maps? A slice is simply a portion of an array and, in fact, always has a "backing array" to which it points, either created implicitly (e.g. with the built-in append() function) or assigned to specific elements of an array explicitly. Arrays always have a fixed length, slices can grow or shrink. A map is like the TDictionary type in Delphi and can be used to create an index of any type to a value of any other type. Here are some examples of these types:

// declare an array of 10 numbers
var nums [10]int

// declare a slice of integers; then append the first five elements (0 to 6-1) from the nums array
var nums5 []int
nums5 = nums[0:6]

// declare a map of three-letter acronyms, initialized with 3 entries
dict := map[string]string {
  "TLA": "Three-Letter Acronym",
  "API": "Application Programming Interface",
  "AFK": "Away From Keyboard",

Slices are more powerful and flexible than you might at first think--and the topic gets deep quickly.

A struct is like the record type in Delphi: it is a user-defined type containing fields. However, structs cannot contain functions; instead, they are associated with a struct type in the function's declaration like this:

type person struct {
  fname string
  lname string
  age int        

func (p person) fullname() string {
  return p.fname + " " + p.lname

The "fullname" function is associated with the "person" type and contains an implicit parameter of type "person" that can be used within the function. When considering Delphi's class or record types, this seems rather loosely connected--but it works and it's simple. (Simplicity is an overarching goal of the Go language.)

An interface in Go is very similar to Delphi's interface type in that it is an abstract type that declares a list of function names that must be implemented but has no implementation itself.

For example, the following establishes a "printer" type listing one function, print(), which must be implemented and associated with a struct for the struct to be considered as adhering to the set of "printer" types:

type printer interface {

Let's say we have a store with books and games. We could create a couple of different types and as long as each implements print(), then they will both implicitly match the printer interface:

type book struct {
  title string
  author string
  price float

func (b book) print() {
  fmt.Printf("%s, by %s - %.2f\n", b.title,, b.price)

type game struct {
  title string
  category string
  price float

func (g game) print() {
  fmt.Printf("%s [%s] - %.2f\n", g.title, g.category, g.price)

Now, if you had an array of printer interfaces loaded with various book and game items, you could call the print() function and it would call the appropriate one for the element's type:

  var inventory [100]printer
  // ...

I found it interesting that you don't actually declare the book or game types as being of the printer interface, just the fact the associated function print() exists is all it takes to qualify. It's design provides for simple dependency injection.

A couple of important aspects of the struct type I should mention before moving on is that structs cannot inherit from other structs but struct fields can be of any type, including another defined struct type; a struct with a field of another struct is called "embedding" and there are some shortcuts for accessing fields that make working with them simple--as you would expect with Go.

Here's an example of a struct type with an embedded struct field referencing the "person" struct type I showed earlier:

type employee struct {
  p person
  title string
  salary float

With this, you can reference the fname and lname fields (defined by the p person field) as if they were native to the employee struct.

There is no "class" type in Go; the struct with embedded sub-types and associated functions and its use of an interface to identify related functions as depicted above are what Go considers all you need to support object-oriented programming. But Go doesn't really try to classify itself as a standard OOP language; indeed, it's FAQ says this, "Although Go has types and methods and allows an object-oriented style of programming, there is no type hierarchy. The concept of “interface” in Go provides a different approach that we believe is easy to use and in some ways more general." While going through the Go course, I kept looking for stronger implementations of how I learned object-oriented programming but as I read articles about why the language was designed like it is, I started to think that maybe all the OOP syntax I've come to expect and work with is more of a restriction than protection. Like I said earlier, it works and it's simple.

Notice the lack of a "date" or "time" type? A time struct is defined in the standard "time" package--it's not a native type.

Flow Control

The if control structure is nearly identical to the C/C++/C# way of doing things. Its initialization clause can declare and initialize a variable:

if x := f(); x < y {
  return x
} else if x > z {
  return z
} else {
  return y

The switch statement is also similar to the C class of languages, but with a couple of simplifying enhancements. The Boolean expression part of the syntax can be preceded by an assignment:

switch s := getinput(); s {
  case "one": return 1
  case "two": return 2
  default: return 0

The expression can also be left out completely which means the case statements need full Boolean expressions of their own:

switch {
  case x < y: return "less"
  case x > y: return "more"
  case x == y: return "equal"

Switch statements in Go can also test a variable's type:

switch x.(type) {
  case int32, int64: return "integer"
  case bool: return "boolean"
  case string: return "string"

Notice no break statements? In Go, each case has an implicit "break" at the end before the next case. However, you can override that with the fallthrough statement which resumes control with the next case:

switch num := number(); {
  case num < 50:
    fmt.Printf("%d is less than 50\n", num)
  case num < 100:
    fmt.Printf("%d is less than 100\n", num)
  case num < 200:
    fmt.Printf("%d is less than 200", num)
    fmt.Printf("%d is out of range", num)

Personally, I find this a much smarter approach than C's break as it's far more common to handle each case separately; fallthrough is most assuredly the rarer case, thus Go's switch syntax is cleaner.

The only looping construct in Go is the for statement--there is no repeat-until, while-do, or do-while; the flexibility of Go's for allows you to do it all with this one statement and its variations.

We'll start with the standard C-like syntax that initializes a variable and repeats the block of code until a condition is false and incrementing at the end of each loop:

for i := 1; i < 10; i++ {

To implement a "while" loop, provide only a conditional:

for getDigit() != 0 {

You can even write an infinite loop, assuming somewhere in the block is a break or return statement:

for {

A Complete Program

Those are the basics. Before I close, I'll share a complete Go console program, explanation will follow:

package main

import (

func main() {
  if len(os.Args) != 2 {
    fmt.Println("Usage: go run readfile.go <filename>")

  filename := os.Args[1]
  file, err := os.Open(filename)
  if err != nil {
    fmt.Println("Error opening file:", err)
  defer file.Close()

  scanner := bufio.NewScanner(file)
  for scanner.Scan() {

  if err := scanner.Err(); err != nil {
    fmt.Println("Error reading file:", err)

In this example, the program first checks if the number of command-line arguments is exactly 2 (the program name and the filename). If not, it prints an error message and exits with a status code of 1.

Then, it opens the file specified by the filename passed on the command-line, reads the lines in the file and prints each line; if there's an error reading the file, it prints an error message and exits with a status code of 1.

Finally, the file is closed using defer so that it will be closed even if there's an error or panic in the program.


There are many other interesting tidbits about the language, such as "named" versus "positional" parameters, the defer keyword, using range to iterate over a collection of items such as an array or slice, and how to recover from a call to panic. My introduction to the language was a fun learning experience. I'm impressed with this language but not sure yet how much I'll do with it as Delphi is my bread-and-butter. As stated earlier, I do intend to use it in conjunction with migrating Drupal sites to Hugo using open-source migration tools written in Go but I still don't know how to access databases or build a GUI application. I have a ways to go and not as much spare time as I'd like.

Add new comment

The content of this field is kept private and will not be shown publicly.
Please enter the characters shown; no spaces but it is case-sensitive.
Enter the characters shown in the image.