learn

namespace vs struct vs class — Rust의 분해된 디자인

셋의 본질

개념역할인스턴스
namespace이름의 그룹화 (충돌 방지 + 조직화)❌ 없음
struct데이터의 묶음 (필드를 한 단위로)✓ 있음
class데이터 + 행동 + 상속의 통합 (OOP 단위)✓ 있음

가장 결정적 차이 한 줄: namespace는 인스턴스가 없다. 코드 조직의 컴파일 타임 개념일 뿐. struct/class는 메모리에 살아있는 데이터 표현.

핵심 비교

특성namespacestructclass
인스턴스 생성
데이터 필드
메서드언어에 따라
상속❌ (대체로)
메모리 위치런타임 없음stack/heap보통 heap
주된 역할이름 충돌 방지데이터 묶음데이터 + 행동 + 다형성

언어별 채택 방식

언어namespacestructclass
C❌ (prefix로 흉내)
C++namespace✓ (class와 거의 동일)
Javapackage❌ (record로 보강)
Python✓ module/package❌ (@dataclass로 흉내)
Rustmod❌ (분해됨)

C++의 struct와 class는 거의 동일 — default 접근 제어자가 public(struct)/private(class)인 차이뿐. 진짜 다른 건 namespace.

Java는 데이터만 묶고 싶어도 class를 만들어야 해서 boilerplate가 많다는 비판을 받아 record를 도입.

Rust의 분해

Rust 디자인의 핵심은 "class라는 큰 추상화를 더 작고 독립적인 개념들로 분해했다". 다른 언어가 class에 묶어둔 기능들을 따로따로 떼어내 책임이 명확한 메커니즘들로 만든 것.

다른 언어의 class가 하던 일Rust 담당
데이터 묶기struct
데이터에 행동 붙이기impl 블록
공통 인터페이스/다형성trait
이름 충돌 방지 / 코드 조직mod
상속(없음, composition + trait 대체)
// 1. mod — namespace 만들기
mod geometry {

    // 2. struct — 데이터 묶음
    pub struct Rectangle {
        pub width: f64,
        pub height: f64,
    }

    // 3. impl — 데이터에 행동 붙이기
    impl Rectangle {
        pub fn new(w: f64, h: f64) -> Self {
            Rectangle { width: w, height: h }
        }
        pub fn area(&self) -> f64 {
            self.width * self.height
        }
    }

    // 4. trait — 공통 인터페이스
    pub trait Shape {
        fn area(&self) -> f64;
    }

    // 5. trait 구현 (데이터 정의와 분리)
    impl Shape for Rectangle {
        fn area(&self) -> f64 {
            self.width * self.height
        }
    }
}

같은 도메인을 Java로:

package geometry;

interface Shape { double area(); }

public class Rectangle implements Shape {
    public double width, height;
    public Rectangle(double w, double h) { this.width = w; this.height = h; }
    public double area() { return width * height; }
}

겉보기엔 비슷하지만 개념의 분리도가 다르다. Java의 class Rectangle은 데이터·메서드·인터페이스 구현을 한 선언에 다 넣지만, Rust는 데이터(struct), 메서드(impl), trait 구현(impl Trait for Type) 이 분리된다.

분해의 실용적 이점 — 직교성

기존 타입에 새 trait 구현 추가 가능. Java에서 String 클래스에 새 인터페이스 구현을 추가하려면 표준 라이브러리 소스를 고쳐야 한다(불가능). Rust는 자기 코드에 impl MyTrait for String { }이라고 적기만 하면 된다 — 데이터(struct)와 행동(impl)이 분리됐기 때문에 데이터 정의를 안 건드리고도 행동을 추가할 수 있다.

같은 데이터에 여러 impl 블록. struct 하나에 여러 impl 블록이 가능해서 책임별로 코드를 나눌 수 있다. 일반 메서드는 한 블록, trait 구현은 다른 블록.

namespace가 정말 namespace에만 집중. Java처럼 디렉토리 구조에 강제로 묶이지 않고, mod는 inline으로도 둘 수 있고, privacy 경계 역할도 명시적이다.

is-rust-object-oriented에서 본 "trait 지향(trait-oriented)" 결론이 여기서 구체화된다. Rust가 OOP를 거부한 게 아니라 OOP 개념들을 묶지 않고 분해해서 제공한 것이다. 사용자는 각 부품을 골라 조립해 OOP 스타일로도 짤 수 있고, 다른 스타일에선 부품을 다르게 쓸 수 있다.

결론

namespace는 이름의 그룹(인스턴스 없음), struct는 데이터의 묶음(인스턴스 있음, 데이터 위주), class는 데이터+행동+상속의 통합형. Rust는 class를 명시적으로 거부하고 그 기능을 struct(데이터) + impl(행동) + trait(인터페이스) + mod(namespace) 네 개의 직교한 메커니즘으로 분해했다. Java/C++의 class 한 덩어리가 Rust에선 네 개의 작은 개념으로 풀어진 셈이며, 처음엔 번거로워 보여도 각 부품을 독립적으로 조립할 수 있다는 유연함을 준다.

관련: what-is-namespace, what-are-methods-and-associated-functions, what-is-rust-enum, is-rust-object-oriented, rust-module-system-overview