learn

Rust는 객체 지향 언어인가

Rust는 객체 지향 언어인가

C++ 출신이 Rust를 처음 배울 때 자연스럽게 떠오르는 질문이다. The Rust Programming Language 책에도 17장에 "Is Rust an Object-Oriented Programming Language?"라는 챕터가 따로 있을 정도로 미묘한 주제다.

결론부터 말하면 "전통적 OOP는 아니지만, OOP의 일부 특성은 가지고 있다".

먼저, OOP가 뭔데?

질문 자체가 애매하다. OOP의 정의가 사람마다 다르기 때문이다.

  • 느슨한 정의: 캡슐화 + 상속 + 다형성 (학교에서 배우는 그것)
  • Alan Kay의 원래 정의: 객체끼리 메시지를 주고받는 것 (Smalltalk 철학)
  • GoF/실무 정의: 데이터와 동작을 객체에 묶고, 다형성으로 유연하게 조립

이 셋 중 어느 기준을 쓰느냐에 따라 답이 달라진다.

Rust가 가진 OOP 특성

캡슐화 ✅

C++의 class처럼 데이터와 동작을 묶고, pub 키워드로 가시성을 제어한다.

pub struct Counter {
    count: u32,  // private (기본값)
}

impl Counter {
    pub fn new() -> Counter { Counter { count: 0 } }
    pub fn increment(&mut self) { self.count += 1; }
}

C++의 class와 거의 같은 역할이다. 단지 struct로 부를 뿐이다.

다형성 ✅ (단, 다른 방식으로)

what-is-trait|trait를 통해 다형성을 구현한다. C++의 가상 함수와 인터페이스를 합친 느낌이다.

trait Drawable {
    fn draw(&self);
}

// 어떤 타입이든 Drawable trait만 구현하면 같이 다룰 수 있음
fn draw_all(items: Vec<Box<dyn Drawable>>) {
    for item in items {
        item.draw();
    }
}

이게 C++의 virtual table과 본질적으로 같다. Rust에서는 dyn Trait이 dynamic dispatch이고, impl Trait나 제네릭이 static dispatch에 해당한다.

Rust에 없는 OOP 특성

상속 ❌

이게 결정적인 차이다. Rust에는 클래스 상속이 없다. C++의 class Dog : public Animal 같은 문법이 아예 존재하지 않는다.

대신 composition + trait으로 대체한다.

// 상속 대신 composition
struct Dog {
    animal: Animal,  // "is-a" 대신 "has-a"
    breed: String,
}

// 공유 동작은 trait으로
trait Bark {
    fn bark(&self) { println!("Woof!"); }  // default impl
}

impl Bark for Dog {}

데이터 재사용은 struct 안에 다른 struct를 넣는 composition으로, 동작 재사용은 trait의 default method로 처리한다.

왜 상속을 뺐을까

이게 진짜 흥미로운 부분이다. C++에서 상속이 일으키는 문제들을 의도적으로 피한 것이다.

  1. 다이아몬드 문제 — A → B, A → C, B와 C → D 일 때, D는 A를 두 번 갖는가? C++은 virtual 상속으로 풀지만 복잡해진다.
  2. fragile base class — 부모 클래스를 수정하면 자식 클래스가 예기치 않게 깨진다
  3. 강한 결합 — 부모-자식 관계는 컴파일 타임에 고정되어 유연성이 떨어진다
  4. "is-a"의 남용 — 코드 재사용 목적으로 상속을 남발하면서 부적절한 계층 구조가 만들어지는 패턴

Rust는 이 문제들을 보고 **"동작 재사용은 trait, 데이터 재사용은 composition으로 하자"**라고 결정했다. trait의 default method가 코드 재사용을 담당하고, struct composition이 데이터 재사용을 담당한다.

이는 사실 C++ 커뮤니티에서도 오래 전부터 권장되던 원칙이다. Effective C++, GoF 모두 *"Prefer composition over inheritance"*를 말해왔다. Rust는 이걸 언어 차원에서 강제한 셈이다.

그럼 Rust는 무슨 패러다임인가

Multi-paradigm이다. 굳이 분류하자면:

  • 함수형 영향: what-is-expression-based-language|expression-based, 불변성 기본, pattern matching, closures, Option/Result 같은 ADT
  • OOP 영향: 캡슐화, 다형성(trait), 메서드 문법
  • 시스템 언어 영향: zero-cost abstraction, manual memory management(ownership)

C++도 multi-paradigm이지만 **"OOP를 중심으로 다른 것도 가능"**한 느낌이라면, Rust는 "trait 기반 추상화를 중심으로 OOP스럽게도 함수형으로도 쓸 수 있는" 느낌이다.

비교 정리

특성C++Rust
캡슐화class/struct + access modifierstruct + pub
다형성virtual function, templatetrait (dyn Trait, generic)
코드 재사용상속trait default method
데이터 재사용상속composition (struct 내장)
다중 상속가능 (다이아몬드 문제 동반)불가 (trait은 여러 개 구현 가능)

한 줄 요약

Rust는 OOP의 캡슐화·다형성은 받아들였지만, 상속은 거부하고 composition + trait으로 대체했다. 그래서 *"객체 지향이냐?"*는 질문에 Rust는 **"우리는 trait 지향(trait-oriented)이다"**에 더 가깝다.