Thomas Wileman

Machine Learning Engineer & Technical Writer

Comparing Python to Go, Rust, and JavaScript: Object Methods

We first examined how Python, Go, Rust, and JavaScript handle class instantiation. The items that stick with me from that assessment were how Rust separates data structures from methods and Go's strict type system.

In this second look, we'll see how these languages use simple methods to operate on instantiated objects. For each language we'll implement the following types of methods in the Person class we introduced previously:

  • A getter method that accesses the person's age
  • A setter method that modifies the person's age
  • A method that modifies the state of an object by increasing their age
Getter Methods
Setter Methods
State Modification
Object Initialization
Python Implementation
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    # Getter method
    def get_age(self):
        return self.age
    # Setter method
    def set_age(self, new_age):
        self.age = new_age
    # Method that modifies state
    def have_birthday(self):
        self.age += 1
        return f"Happy birthday! Now I'm {self.age}"
# Usage
person = Person("Alice", 30)
print(person.get_age())           # 30
print(person.have_birthday())     # Happy birthday! Now I'm 31
person.set_age(25)
print(person.get_age())           # 25

Python Analysis

  • The first thing I notice is how clean and readable Python is. The logic behind each method is easily understandable.
  • Again, we see that variable types (name and age) don't require data types when defined. Python figures datatypes out at runtime which makes it easier during development but may result in runtime errors that other languages might have caught earlier.
  • The setter method, set_age, makes it easy to change the age of an established Person object.
  • Similarly, the have_birthday method shows us how easy Python can modify an established state.
  • The getter method, get_age, is redundant. We could just reference the age attribute of an instantiated object.
Immutable Borrowing (&self)
Mutable Borrowing (&mut self)
Struct/Impl Separation
Explicit Type System
Rust Implementation
struct Person {
    name: String,
    age: u32,
}
impl Person {
    // Constructor
    fn new(name: String, age: u32) -> Person {
        Person { name, age }
    }
    // Getter method (borrows self)
    fn get_age(&self) -> u32 {
        self.age
    }
    // Setter method (mutable borrow)
    fn set_age(&mut self, new_age: u32) {
        self.age = new_age;
    }
    // Method that modifies state
    fn have_birthday(&mut self) -> String {
        self.age += 1;
        format!("Happy birthday! Now I'm {}", self.age)
    }
}
// Usage
let mut person = Person::new("Alice".to_string(), 30);
println!("{}", person.get_age());              // 30
println!("{}", person.have_birthday());        // Happy birthday! Now I'm 31
person.set_age(25);
println!("{}", person.get_age());              // 25

Rust Analysis

  • Off the bat, the struct/impl pattern separates data definition from behavior. This means that when the code compiles, Rust doesn't have to guess about data types or lookup which function to apply to the object. Datatypes are already resolved and the compiler directly inserts the function call.
  • Rust uses &self and &mut self for memory management. &self reads instantiated object data but doesn't change it. &mut self reads and modifies instantiated data. Python doesn't have this. Instead Python uses a garbage collector to track memory. This makes it easy to use but slow. Rust enforces ownership rules at compile time to manage memory.
Pointer Receivers (*Person)
Method Receivers
Explicit Typing
Constructor Convention
Go Implementation
package main
import "fmt"
type Person struct {
    Name string
    Age  int
}
// Constructor function
func NewPerson(name string, age int) *Person {
    return &Person{Name: name, Age: age}
}
// Getter method
func (p *Person) GetAge() int {
    return p.Age
}
// Setter method
func (p *Person) SetAge(newAge int) {
    p.Age = newAge
}
// Method that modifies state
func (p *Person) HaveBirthday() string {
    p.Age++
    return fmt.Sprintf("Happy birthday! Now I'm %d", p.Age)
}
// Usage
func main() {
    person := NewPerson("Alice", 30)
    fmt.Println(person.GetAge())           // 30
    fmt.Println(person.HaveBirthday())     // Happy birthday! Now I'm 31
    person.SetAge(25)
    fmt.Println(person.GetAge())           // 25
}

Go Analysis

  • The *Person and &Person give you control over memory. The & means "address of" and allows you to allocate memory. The * means that a function points to a specific Person object.
Dynamic 'this' Binding
Constructor Function
Property Access Methods
Template Literals & Mutation
JavaScript Implementation
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    // Getter method
    getAge() {
        return this.age;
    }
    // Setter method
    setAge(newAge) {
        this.age = newAge;
    }
    // Method that modifies state
    haveBirthday() {
        this.age++;
        return `Happy birthday! Now I'm ${this.age}`;
    }
}
// Usage
const person = new Person("Alice", 30);
console.log(person.getAge());           // 30
console.log(person.haveBirthday());     // Happy birthday! Now I'm 31
person.setAge(25);
console.log(person.getAge());           // 25

JavaScript Analysis

  • Again, the syntax is very similar to Python. No type declarations, OOP class syntax, and easily understandable string handling. Further reading makes it clear that JavaScript is meant for web development. They share similar design philosophies: readable, maintainable code, rapid development support.

Key Takeaways

Rust's ownership system, &self and &mut self, and Go's memory pointer system, &Person and *Person, give you explicit control over memory. However they handle data concurrency differently. Rust won't compile if there are type errors or data races. On the other hand Go doesn't prevent a data object from being accessed by two operations at the same time. You have to use additional tools to enforce sequential operations.

Python and JavaScript employ some of the same philosophies but for different use cases (Python for data applications and JavaScript for web applications).