One of the biggest mindset shifts I've had while learning Go came when I reached functions and methods.
As a Ruby developer, almost everything revolves around objects. Every piece of behavior belongs to a class, and every function is really just a method attached to an object.
Go takes a different approach.
Functions exist independently, and methods are simply functions with a receiver. There are no classes, no inheritance, but still writing object-oriented code.
Let's see how Go approaches behavior compared to Ruby.
Why This Felt Strange Coming from Ruby
In Ruby, we rarely think about standalone functions because they don't really exist.
Everything is an object.
class Calculator
def add(a, b)
a + b
end
end
calculator = Calculator.new
calculator.add(2, 3)
Enter fullscreen mode Exit fullscreen mode
Want behavior?
Create a class.
Need reusable logic?
Another class or a module.
Go starts from a much simpler place.
Most behavior begins as just... a function.
Functions in Go
Functions are declared using the func keyword.
func add(a int, b int) int {
return a + b
}
Enter fullscreen mode Exit fullscreen mode
The syntax may look unusual at first, especially if you've spent years writing Ruby.
Instead of writing the return type first, Go places it after the parameter list.
Calling the function is exactly what you'd expect.
result := add(2, 3)
Enter fullscreen mode Exit fullscreen mode
Grouping Parameters
Go lets you group parameters that share the same type.
Instead of writing:
func add(a int, b int) int
Enter fullscreen mode Exit fullscreen mode
you can write:
func add(a, b int) int {
return a + b
}
Enter fullscreen mode Exit fullscreen mode
It's a small feature, but it keeps function declarations much cleaner.
Returning Multiple Values
func divide(a, b int) (int, int) {
return a / b, a % b
}
Enter fullscreen mode Exit fullscreen mode
Then you can capture both results.
quotient, remainder := divide(10, 3)
Enter fullscreen mode Exit fullscreen mode
Coming from Ruby, this felt familiar because Ruby also supports multiple assignment.
def divide(a, b)
[a / b, a % b]
end
quotient, remainder = divide(10, 3)
Enter fullscreen mode Exit fullscreen mode
The difference is that in Ruby we're really returning an array, while in Go multiple return values are part of the language itself.
Named Return Values
One feature I wasn't expecting was named return values.
Instead of writing:
func sum(a, b int) int {
return a + b
}
Enter fullscreen mode Exit fullscreen mode
you can write:
func sum(a, b int) (result int) {
result = a + b
return
}
Enter fullscreen mode Exit fullscreen mode
Notice the bare return.
Because result was named in the function signature, Go automatically returns it.
This is called a naked return.
Anonymous Functions
Go also supports anonymous functions.
You can assign them to variables.
greet := func(name string) {
fmt.Println("Hello", name)
}
Enter fullscreen mode Exit fullscreen mode
Or execute them immediately.
func() {
fmt.Println("Running immediately")
}()
Enter fullscreen mode Exit fullscreen mode
This reminded me a lot of Ruby blocks and lambdas.
greet = ->(name) { puts "Hello #{name}" }
greet.call("Alice")
Enter fullscreen mode Exit fullscreen mode
Although the syntax is different, the concept feels familiar.
Variadic Functions
Sometimes you don't know how many arguments a function should accept.
Go solves this using variadic parameters.
func printNumbers(numbers ...int) {
}
Enter fullscreen mode Exit fullscreen mode
The ... tells Go to collect all arguments into a slice.
Ruby has something very similar.
def print_numbers(*numbers)
end
Enter fullscreen mode Exit fullscreen mode
This was one of the easiest concepts for me to understand because the two languages approach it almost the same way.
So Where Are the Classes?
This was the question I kept asking while reading through the documentation.
If Go doesn't have classes...
Where do methods live?
The answer surprised me.
Methods belong to types, not classes.
Methods in Go
A method is simply a function with a receiver.
type Person struct {
Name string
}
func (p Person) SayHello() {
fmt.Println("Hello,", p.Name)
}
Enter fullscreen mode Exit fullscreen mode
Now we can call it like this.
person := Person{Name: "Alice"}
person.SayHello()
Enter fullscreen mode Exit fullscreen mode
The syntax felt unusual at first.
Instead of defining the method inside the type like Ruby does, the receiver appears before the method name.
func (receiver Type) MethodName() {
}
Enter fullscreen mode Exit fullscreen mode
Value Receivers
By default, methods receive a copy of the value.
func (p Person) Rename(name string) {
p.Name = name
}
Enter fullscreen mode Exit fullscreen mode
Changing p.Name only changes the copy.
The original object stays exactly the same.
This is called a value receiver.
Pointer Receivers
If you want to modify the original value, you use a pointer receiver.
func (p *Person) Rename(name string) {
p.Name = name
}
Enter fullscreen mode Exit fullscreen mode
Now changes affect the original struct.
This distinction between value and pointer receivers was one of the first places where Go reminded me of C++.
Ruby developers usually don't think about this because objects are already passed around by reference-like semantics.
Go asks you to decide whether you're working with a copy or the original value.
Side-by-Side Comparison
| Go | Ruby |
|---|---|
| Standalone functions | Everything is a method |
func keyword |
def keyword |
| Multiple return values | Usually arrays or multiple assignment |
| Anonymous functions | Lambdas and Procs |
Variadic parameters (...) |
Splat operator (*) |
| Methods attached to types | Methods defined inside classes |
| Value receivers | Objects usually mutated directly |
| Pointer receivers | Reference-like object semantics |
Thoughts
Ruby's object model is incredibly elegant.
Everything feels consistent because everything is an object.
Go takes a more lightweight approach.
Instead of encouraging you to wrap every behavior inside a class, it asks a simple question:
Does this really need to belong to a type?
Sometimes the answer is yes—a method makes sense.
Other times, a standalone function is perfectly fine.
Go is trying to remove the parts it considers unnecessary while keeping the parts that are genuinely useful.
This has been one of the most interesting lessons so far in my Go journey, what about you?
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.