Struct vs Class โ Living Without Inheritance
Go's Struct Embedding Instead of Ruby's class < Base
For Ruby developers, class is like air. Everything lives inside classes. class User < ApplicationRecord โ inherit functionality, mix in modules with include.
Go has no class and no inheritance. Structs define data structures, and methods attach to those structs. That's it.
Ruby's class vs Go's struct
Ruby: class User; attr_accessor :name, :email; end
Go: type User struct { Name string; Email string }
Looks similar but fundamentally different. Ruby's class is a vessel for instance variables, methods, inheritance, mixins. Go's struct is just a bundle of data fields. Methods attach from outside.
No Inheritance โ Embedding Instead
class Admin < User is impossible in Go. Instead, embed one struct inside another:
type Admin struct {
User // embedding โ brings all User fields and methods
Role string
}
Admin didn't "inherit" User โ it "contains" User. admin.Name accesses User's field, and User's methods are callable directly.
Similar to Ruby's include UserModule. But Go's embedding happens at the type level with zero runtime overhead.
No new
Ruby: User.new(name: "kim") โ calls initialize. Go has no constructor. User{Name: "kim"} creates a literal, or by convention you write a NewUser("kim") factory function.
private/public by Case
Instead of Ruby's private, protected, public keywords, Go uses the first letter. Uppercase = exported (accessible from outside). Lowercase = package-internal. User.Name is accessible, User.name is not.
Ruby to Go
Ruby: class < Base inheritance โ Go: struct embedding (composition)
Ruby: attr_accessor โ Go: struct fields (uppercase=public, lowercase=private)
Ruby: User.new(initialize) โ Go: no constructor, User{} literal or NewUser() factory
Ruby: include Module โ Go: struct embedding (similar but resolved at compile time)
Pros
- ✓ Embedding enables code reuse without inheritance complexity (diamond problem, etc.)
- ✓ Access control via case alone โ no need for private/public keywords
Cons
- ✗ No dynamic dispatch (method_missing, respond_to?) โ no metaprogramming
- ✗ No constructor โ initialization validation must be done manually in factory functions