Now I’m revisiting an old post of mine about my personal impressions about the Go language.
My first impression on Go was very wrong. I flanked Go from the Python and Node.js perspective, which was very disappointing. When you try to use a language that offers powerful tools to access system resources with an open-and-shut mind, the result is frustrating.
As soon as I understood Go is closer to C++ and Objective-C, I realised it’s a cool, powerful, and fun programming language.
At that time, a friend vainly tried to explain me Go pointers as read-write parameters against read-only ones. If he had simply told me “pointers like in C,” I’d get at once.
My disapproval to Go is that Alphabet might not have recreated the wheel so hard, it could take advantage of other well-tested platforms, like Vala and C♯ did. It would entice lotta god programmers by the soft learning curve (despite Linus Tovarlds disagrees).
In Go, interfaces look like structural types in Scala.
In other hand, structs are much like
Objective-C @interface
s.
An instance of any struct that matches an interface is instance of the inteface too, just like structural types.
For sample, take the following interface:
type Person interface {
FirstName() string
LastName() string
FullName() string
Birth() time.Time
}
If one has defined a struct:
type personType struct {
firstName, lastName string
birth time.Time
}
And then every function from Person
is implemented for personType
:
func (p personType) FirstName() string {
return p.firstName
}
func (p personType) LastName() string {
return p.lastName
}
func (p personType) FullName() string {
return strings.Trim(fmt.Sprintf("%v %v", p.firstName, p.lastName), " ")
}
func (p personType) Birth() time.Time {
return p.birth
}
Then it’s possible to return a personType
as Person
:
func NewPerson(firstName, lastName string, birth time.Time) Person {
return personType{firstName, lastName, birth}
}
For testing, Go’s got a built-in testing library, that’s very easy to use.
Being very verbose, a yet simple example may be:
func TestPerson(t *testing.T) {
timeForm := "2006-01-02"
birth, _ := time.Parse(timeForm, "2017-06-12")
p := NewPerson("John", "Doe", birth)
t.Run("primary methods", func(t *testing.T) {
t.Run("FirstName", func(t *test.T) {
if got := p.FirstName(); got != "John" {
t.Fatalf("expected John, got %v", got)
}
})
t.Run("LastName", func(t *test.T) {
if got := p.LastName(); got != "Doe" {
t.Fatalf("expected Doe, got %v", got)
}
})
t.Run("Birth", func(t *test.T) {
expected, _ := time.Parse(timeForm, "2017-02-12")
if got := p.Birth(); got != expected {
t.Fatalf("expected %v, got %v", expected, got)
}
})
})
t.Run("secondary methods", func(t *test.T) {
t.Run("FullName", func(t *test.T) {
if got := p.FullName(); got != "John Doe" {
t.Fatalf("expected John Doe, got %v", got)
}
})
})
}
Enough for most cases.
In order to understand Go time format: Format a time or date.
The Go’s exception catching pattern is returning the error as second argument for every function that can go wrong.
In the sample above, one can found this line:
birth, _ := time.Parse(timeForm, "2017-06-12")
The underscore (_
) means the exception is discarded. If one wanna deal with
the exception, one can do something like:
birth, err := time.Parse(timeForm, "2017-06-12")
if err != nil {
// Deal with the exception
...
}
Another way Go can throw an exception is by a panic attack. One can do it by
calling the panic()
function.
The only way to catch this kind of exception is calling recover()
inside a
defer
block.
One can catch the exception and return it through a writable channel (chan
or
chan<-
).
It smells like:
func DoSomethingDangerous(res chan<- error) {
defer res <- recover()
// Do somenthing that can raise a panic attack.
...
}
Some useful resources:
Also in DEV.to.