Coming from the Rubyland and world of iOS, learning Go was an interesting experience. I've thought it might be worth to share some things learnt on the way, and put up the simple transition guide for everyone thinking about giving Go a shot.
This post is not the most idiomatic Ruby or Go you'll read, but it's instead written in a way to point similarities between the two.
What's the reason people want to develop applications in Go? Here are some of mine:
Ruby is a wonderful language. I love writing it and there's thousands of people that do as well. What I don't fancy with Ruby is deploying. Deploying your own web apps is something you can still deal with, but shipping a tool to many people is a hassle. They need to have the correct Ruby installed, all the gems fetched, and often you need to support Rubies down to 1.8.7.
I highly encourage you to check out ngrok. After you download it and get your mind blown, just think of this: you've downloaded a tunneling proxy server, that ships with HTML frontend and bootstrap. In one binary. No dependencies. You've just run it without installing anything.
Go has built-in testing, code coverage, basic dependency fetcher, go get
,
unified code formatter, go fmt
/ go imports
, cross-compiler, and many other
tools. You can compile Go from your Mac for all the platforms, including Windows.
Most of Go's tools are standalone CLI apps, that can give any editor some
serious IDE capabilities.
Finally, Go is a way smaller language than Ruby or ObjectiveC, so there's less things to learn. Once you understand the basics, you'll be able to read mostly all the Go code you find online. Let's take a look.
But don't worry. You can achieve lots with structs.
1 2 3 4 5 6 7 |
|
1 2 3 4 5 6 7 8 9 10 |
|
You can make Go look more Object-Oriented by adding functions to structs. The following usage example demonstrates so:
1 2 3 |
|
1 2 3 |
|
Go methods could be a bit hard to read at first. But it's really easy to do so, if you consider that all the Go functions are written the same way:
func
keywordThe simplest Go function doesn't declare input, output or a receiver.
1 2 3 |
|
If you want to achieve the Ruby example from above, you need to add the Start() function on the Engine (receiver)
1 2 3 4 |
|
Now by nature, this function doesn't return anything. Let's say you want it to return an error if car doesn't start:
1 2 3 4 |
|
Finally, let's say you want to delay starting by 2s
1 2 3 4 |
|
As we've mentioned before, there's no classes and objects in Go, but there
are structs. Let's try to declare that Car
example from above, both in Ruby
and Go.
In Ruby, we'll make Car and Engine classes, and define methods inside them.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
In Go, we'll make CarEngine and Car structs, and define functions on them.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
There's no such a private
keyword, or public static void main()
in Go.
In fact, doing so is pretty simple:
- private stuff starts with lowercase
- public starts with uppercase
That applies on the following examples, respectively:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Go does have first class functions, and you can use them as such:
1 2 3 |
|
1 2 3 |
|
Some people get afraid of types. And I have to admit - it is a bit confusing after getting so comfortable with Ruby. Casting in Go looks (like everything else) like upside-down casting in e.g. Java or C#.
So if you want to convert a string
to byte array
(byte slice in Go, to be
correct), you would type
1 2 |
|
Vice-Versa
1 2 |
|
Notice how parenthesies positioning is different from Java or C#,
(string)bytes
vs string(bytes)
. The array []
notation goes on the opposite side as well.
Instead of byte[]
, you type []byte
.
This was one of the biggest challenges when I've started writing a web service. The concept is mostly this:
map
(Hashmap) that's not a first-class objectLet's assume the JSON we'll try to work with looks like this:
1 2 3 4 5 6 7 8 9 |
|
If you want to deserialize this into first-class models, you need 2 structs:
1 2 3 4 5 6 7 8 9 10 |
|
If you try to Unmarshal
this directly, the User
won't deserialize properly.
To demonstrate the problem, we can go the other way - make a model
programatically and Marshal
it to JSON.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
1 2 3 4 5 6 7 8 9 |
|
If you take a look at the generated JSON, it doesn't look the same as above;
the fields have capitalized keys, so we need to map them. Don't get scared of
backticks. They're something similar to Ruby's 'string', %Q(string)
and many
other ways you can generate a string. In this case, they allow you to use
quotes for JSON keys.
1 2 3 4 5 6 7 8 9 10 |
|
Awesome, we're ready to go. Now serializing and deserializing JSON to structs works as expected. In the real life, you'll mostly be getting this JSON from a HTTP request. Here's a sample factory method that takes a request pointer and returns a new User.
1 2 3 4 5 6 7 8 9 10 11 |
|
The last part is dealing with your own maps. This might be useful when you're doing some request processing and manual serialization. A real life example would be listening to web-hooks from many web services, and mapping them to an uniform interface.
We'll extract request parsing in another method that'll return a
map[string]interface{}
. This is the syntax for the most generic Hashmap
in Go. Basically, it says keys are strings, and values are anything
(strings, numbers, arrays, objects).
Then we'll manually construct the user, as if the mapping didn't exist.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
To wrap up, a more pragmatic way of constructing the user would be passing
everyting into initializer instead of using the new(User)
syntax.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
There's no real meta programming like in Ruby, but there are some tricks
you can make using the reflect
package and the fact Interfaces implementation
is implicit.
To get some ideas, you may want to check out these packages:
Revel - a bit bigger web framework like Rails or Play
Convey - BDD testing framework with live reload and web frontend
Ginkgo - yet another RSpec-style BDD framework
If you've really got this far - here's a bonus: I've mentioned that fancy tooling on the top of the post. There's some neat tools that'll make coding Go feel as you're writing a scripting language like Ruby.
The simplest solution ever is to add the following mapping in Vim - it lets
you hit <leader> g
and watch the current file execute.
1
|
|
If you install this plugin, it'll format your code properly before executing. It'll also check your syntax and fix imports. If you're using an auto-completion engine, it'll provide a full-fledged, context-based autocompletion!
And, if you're writing test-driven app, Convey will re-compile and re-run test each time you hit save.
If you're writing a webapp, gin from codegangsta will reload the server each time you save.
Pretty cool, huh?