(WIP) Ruby To Go: How can I do X in Ruby with Go? (Part I)
I am a programmer who can write decent Ruby and some Javascript.
These two languages were all I know. I wanted to add Go to my list of programming language, so I started learning Go.
It’s boring to read programming books to study programming languages, so I decided to learn Go by porting some programs written in Ruby.
While I was porting Ruby program to Go, I came to think it is useful if there is a cheatsheet that I can loop up in order to convert idiomatic Ruby code to Go.
Ruby and Go are totally different language, so sometimes it is impossible to simply translate Ruby code to Go. However, it is possible in most cases to write Go code that is sematically equivalant to Ruby code.
So, here is a How can I do X in Ruby with Go? cheetsheet.
I hope you find it useful.
Contents of this artcile
Array and Enumerable Operation
- Create array
- Append element to array
- Concatenate arrays
- Create multi dimension array
- Create empty array
- Iterate on array
- Looping N times
- Clone array
- Accessing elements of array by range
- Compare array
- Check if array includes an element
Method Definition
MathematicOperation
Misc
Array and Enumerable Operation
Array is very powerful data structure and enumerable is probably the most frequently used object in Ruby.
In Go, we have two different enumerable data structures: array and slice. I don’t write about details about them since it is not the goal of this post, but array is low-level data structure that slice refers to.
Create array
Ruby:
numbers = [1,2,3]
fruits = ["apple", "banana", "grape"]
Go:
numbers := []int{1,2,3}
fruits := []string{"apple", "banana", "grape"}
In the case of Go, numbers
and words
are slice, not array. Array is primitive data structure, not frequently used in Go code.
If you want to archive similar things to Ruby array, slice should work for you.
Append an element to array
Ruby:
numbers = [1,2,3]
numbers << 4
Go:
numbers := []int{1,2,3}
numbers.append(numbers, 4)
append adds elements to slice and return new slice. Therefore, you have to reassign to itself.
Concatenate arrays
Ruby:
numbers1 = [1,2,3]
numbers2 = [4,5,6]
numbers1 = numbers1 + numbers2
Go:
numbers1 := []int{1,2,3}
numbers2 := []int{4,5,6}
numbers1 = append(numbers1, numbers2...)
...
suffix on the slice indicates that it should be passed as the variadic argument, expanded as each int
elements inside append
.
Thus, this is equivalent to below:
numbers1 := []int{1,2,3}
numbers1 = append(numbers1, 4, 5, 6)
Create multi dimension array
Ruby:
multi_array = [[1,2,3],[4,5,6]]
Go:
var mul [][]int = [][]int{ {1, 2, 3}, {4,5,6} }
Create empty array
Ruby:
array = []
Go:
var array []int
When slice is declared, but not initialised, the slice points to an array of size 0.
Iterate on array
Ruby:
numbers = [1,2,3]
numbets.each {|num| puts num }
Go:
numbers := []int{1,2,3}
for _, num := range numbers {
fmt.Println(num)
}
If you want to access the index while iterating over the slice, replace _
with other variable, for example, i
.
numbers := []int{1,2,3}
for i, num := range numbers {
fmt.Println("index: ", i, "number: ", num)
}
Looping N times
Ruby:
5.times {|num| puts num}
Go:
for num:=0; num <5; num++ {
fmt.Println(num)
}
Clone array
Ruby:
new_array = old_array.clone
Go:
new_array := make([]int, len(old_array))
copy(new_array, old_array)
Accessing elements of array by range
Ruby:
numbers=[1,2,3,4,5]
numbers[0..3]
Go:
ary := []int{1,2,3,4,5}
ary[0:4]
Note that, with from:to
, to
is the index where to end but not including the index itself.
Compare array
Ruby:
if ary1 == ary2
puts "Same array"
end
Go:
same := true
for i, elm:= range ary1 {
if ary2[i] != r { same = false }
}
if same == true {
fmt.Println("Same slice")
}
You cannot compare slice in Go. You will get slice can only be compared to nil
error if you try to do that.
Check if array includes an element
Ruby:
fruits = ["apple", "banana", "grape"]
if fruits.include?("apple")
puts "include!"
end
Go:
include := false
fruits := []string{"apple", "banana", "grape"}
for _, elm := range fruits {
if elm == "apple" {
include = true
break
}
}
if include == true {
fmt.Println("include!")
}
Method Definition
There are two things that are equivalant to Ruby’s method in Go: method and function. Method is a type of function but requires specific receiver.
Define a method with optional parameter
Ruby:
def greeting(word="hello!")
puts word
end
Go:
This is not possbile in Go. Go does not support optional parameter in function or method definition. One workaround is using struct.
type greetingArg struct { word string }
func greeting(opt greetingArg) {
word := opt.word
if word == "" {
fmt.Println("hello!")
} else {
fmt.Println(word)
}
}
greeting(greetingArg{})
greeting(greetingArg{"bye!"})
Define a method with variable length argument
Ruby:
def foo(*args)
args.each {|arg| puts arg}
end
Go:
func foo(arg ...int) {
for _, arg := range arg {
fmt.Prinln(arg)
}
}
Mathematic Operation
Modular of negative number
Both Ruby and Go supports modular of negative number. However, their behavior is different.
Ruby:
-5 % 3 => 1
Go:
-5 % 3 => -2
Go follows truncated toward zero for the division of negative number.
If you want to get the same value that Ruby returns, here is how to do this.
Go:
divider := 3
mod := -5 % divider
if mod < 0 {
mod = mod + divider
}
Note that values returnd by Ruby and Go are both mathematically correct. It’s just there are two ways to define negative modulo.
Misc
Nil checking
Ruby:
if val.nil?
puts "val is nil"
end
Go:
var str string
if str =="" {
fmt.Println("str is empty")
}
var i int
if i == 0 {
fmt.Println("i is zero")
}
When you declear a variable without intialization, the variable is set to zero value for its type. Below is default zero value for primitive types.
Type | Value |
---|---|
string | ’‘ |
int | 0 |
float | 0.0 |
boolean | false |
pointer | nil |
interface | nil |
slice | nil |
map | nil |
Checking the class
Ruby:
puts "abc".class
Go:
import "reflect"
fmt.Println(reflect.TypeOf("abc"))