구조체와 impl — Ruby의 class가 Rust에서 두 조각으로 나뉜다
struct로 데이터, impl로 메서드, trait로 다형성
Ruby에서 클래스를 만들면 데이터(인스턴스 변수)와 메서드가 한 몸이다. Rust는 이걸 분리했다.
# Ruby — 데이터와 메서드가 한 몸
class User
attr_reader :name, :email
def initialize(name, email)
@name = name
@email = email
end
def display
"#{name} <#{email}>"
end
end
// Rust — 데이터
struct User {
name: String,
email: String,
}
// Rust — 메서드 (별도 블록)
impl User {
fn new(name: String, email: String) -> Self {
User { name, email }
}
fn display(&self) -> String {
format!("{} <{}>", self.name, self.email)
}
}
&self — Ruby의 암시적 self가 명시적으로
Ruby에서 인스턴스 메서드 안에서 self는 자동으로 쓸 수 있다. name만 써도 self.name이다. Rust에서는 첫 번째 인자로 &self를 명시해야 한다.
&self— 읽기 전용 (Ruby의 일반 메서드)&mut self— 값을 변경할 수 있음 (Ruby의!메서드와 비슷)self— 소유권을 가져감 (호출 후 원본 사용 불가)
impl User {
fn name(&self) -> &str { &self.name } // 읽기
fn set_name(&mut self, name: String) { self.name = name; } // 쓰기
fn into_name(self) -> String { self.name } // 소유권 이동
}
associated function — Ruby의 클래스 메서드
&self를 안 받는 함수는 Ruby의 클래스 메서드에 해당한다.
# Ruby
User.create(name: "sehwa") # 클래스 메서드
// Rust
User::new(String::from("sehwa"), String::from("s@e.com")); // :: 로 호출
.이 아니라 ::로 호출한다. new는 특별한 키워드가 아니라 그냥 관례다.
trait — Ruby의 module
Ruby에서 include로 모듈을 섞어 넣듯, Rust에서는 trait을 impl한다.
# 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());
}
}
핵심 차이: Ruby의 module은 구현을 포함할 수 있고 다중 include가 자유롭다. Rust의 trait은 인터페이스에 가깝고, 구현은 impl Trait for Type에서 한다. 다이아몬드 상속 문제가 없다.
derive — 자동 구현
Ruby에서 Comparable을 include하면 <=>만 정의하면 나머지 비교 연산자가 자동으로 생긴다. Rust에서는 derive로 비슷한 걸 한다.
#[derive(Debug, Clone, PartialEq)]
struct User {
name: String,
email: String,
}
Debug는 puts user.inspect 같은 디버그 출력, Clone은 .dup, PartialEq는 == 비교. 어노테이션 한 줄로 자동 구현된다.
상속이 없다
Rust에는 상속이 없다. 이게 Ruby 개발자에게 가장 큰 패러다임 전환이다. 코드 재사용은 composition(구조체 안에 다른 구조체를 넣는 것)이나 trait으로 해결한다.
핵심 포인트
struct로 데이터 정의, impl로 메서드 정의 — 별도 블록
&self = 읽기, &mut self = 쓰기, self = 소유권 이동
trait은 Ruby의 module include에 대응 — 상속 대신 사용
derive로 Debug, Clone, PartialEq 등을 자동 구현
장점
- ✓ 데이터와 행위가 분리되어 구조가 명확하다
- ✓ trait은 다이아몬드 상속 문제가 없다
단점
- ✗ Ruby의 open class(기존 클래스에 메서드 추가)가 안 된다
- ✗ 상속 없이 코드 재사용하는 사고방식 전환이 필요하다