πŸ—οΈ

Structs & impl β€” Ruby's class Splits Into Two Pieces in Rust

struct for data, impl for methods, trait for polymorphism

In Ruby, a class bundles data (instance variables) and methods together. Rust separates them.

# Ruby β€” data and methods in one body
class User
  attr_reader :name, :email
  def initialize(name, email)
    @name = name
    @email = email
  end
  def display
    "#{name} <#{email}>"
  end
end

// Rust β€” data
struct User {
    name: String,
    email: String,
}

// Rust β€” methods (separate block)
impl User {
    fn new(name: String, email: String) -> Self {
        User { name, email }
    }
    fn display(&self) -> String {
        format!("{} <{}>", self.name, self.email)
    }
}

&self β€” Ruby's implicit self made explicit

In Ruby, self is automatic inside instance methods. Just name works as self.name. In Rust, you must explicitly pass &self as the first argument.

  • &self β€” read-only (like Ruby's normal methods)

  • &mut self β€” can modify (like Ruby's ! methods)

  • self β€” takes ownership (original unusable after call)

impl User {
    fn name(&self) -> &str { &self.name }           // read
    fn set_name(&mut self, name: String) { self.name = name; } // write
    fn into_name(self) -> String { self.name }        // ownership move
}

Associated functions β€” Ruby's class methods

Functions without &self are Ruby's class methods.

# Ruby
User.create(name: "sehwa")  # class method

// Rust
User::new(String::from("sehwa"), String::from("s@e.com"));  // called with ::

:: instead of .. new isn't a keyword β€” just convention.

trait β€” Ruby's module

Like Ruby's include mixes in modules, Rust impls traits.

# Ruby
module Printable
  def print_info
    puts to_s
  end
end
class User
  include Printable
end

// Rust
trait Printable {
    fn print_info(&self);
}

impl Printable for User {
    fn print_info(&self) {
        println!("{}", self.display());
    }
}

Key difference: Ruby modules can include implementations and allow free multiple inclusion. Rust traits are closer to interfaces, with implementations in impl Trait for Type. No diamond inheritance problem.

derive β€” automatic implementation

In Ruby, including Comparable and defining <=> gives you all comparison operators for free. Rust's derive is similar.

#[derive(Debug, Clone, PartialEq)]
struct User {
    name: String,
    email: String,
}

Debug is like puts user.inspect, Clone is .dup, PartialEq is ==. One annotation line, auto-implemented.

No inheritance

Rust has no inheritance. This is the biggest paradigm shift for Ruby developers. Code reuse happens through composition (putting structs inside structs) or traits.

Key Points

1

Define data with struct, methods with impl β€” separate blocks

2

&self = read, &mut self = write, self = ownership move

3

trait maps to Ruby module include β€” used instead of inheritance

4

derive auto-implements Debug, Clone, PartialEq etc.

Pros

  • Data and behavior separation makes structure clear
  • Traits have no diamond inheritance problem

Cons

  • Ruby's open class (adding methods to existing classes) isn't possible
  • Mindset shift needed for code reuse without inheritance

Use Cases

Bundling data + business logic like ActiveRecord models Implementing Ruby Comparable/Enumerable as traits