Rust(12) – Struct 구조체 ≈ class (다른언어)

C++은 구조체도 있고, 클래스도 따로있습니다. 요즘은 구조체도 멤버함수가있고 클래스의 은닉성만 갖고있지 않는걸로 알고있는데, Rust에서는 class가 따로 존재하지 않습니다.

먼저 C++과 Rust를 비교해보면 Rust는 아래 표와같이 C++기능을 대응하고있습니다.

C++은 구조체와 클래스를 구분해놓고 클래스가 좀더 많은 기능을 지원하는 정도의 차이만 있습니다. Rust의 구조체는 C++의 구조체와 기본기능이 더 가깝다고 할수있지만 추가 키워드를 활용해 C++에서 할수있는 기능들을 대응하고있습니다. Rust 공식문서에서는 상속구조대신 합성구조를 선택한이유가 메모리 절약을 위해서라고 합니다. 아마도 상속구조가 복잡해지면, 자식 클래스에서 안쓰능기능이나 멤버변수가 점점 늘어나는것을 지적하는거같네요. 참고로 파이썬유저가 있으시다면, 파이썬의 클래스는 C++과 더많이 닮아 있습니다.

RustC++
structstruct
impl 구현멤버함수 method, 생성자
trait 트레이트추상화/다형성
composition 합성상속

Impl 구현

아래는 User라는 구조체를 만들고 impl키워드를통해 생성자이자 멤버함수인 new()를 구현하고있습니다.

struct User {
    active: bool,
    username: String,
    email: String,
    sign_in_count: u64,
}

impl User {
    fn new(username: String, email: String) -> Self {
        Self {
            active: true,
            username,
            email,
            sign_in_count: 1,
        }
    }
}

이렇게 구현하면 인스턴스를 생성할때 User::new()를 사용해 마치 C++의 클래스처럼 직관적으로 할 수 있어요.

let user2 = User::new(String::from("user2"), String::from("user2@example.com"));
println!("{}", user2.email);

그리고 생성자 함수를 보신분은 눈치 채셨겠지만 그냥 생성자없이 초기화 할수도 있습니다. 다만, Rust에서는 멤버변수에 기본값을 정의할수없어서 기본값이 필요한 변수가 있다면 생성자를 만드는게 편합니다.

생성자 없이 초기화

let mut user1: User = User {
        active: true,
        username: String::from("someusername123"),
        email: String::from("someone@example.com"),
        sign_in_count: 1,
    };

멤버변수 재사용

멤버변수 기본값을 지정하지 못하는대신 다른 장점도 있습니다. 아래처럼 ..키워드를 사용하면 명시한 변수를 제외한 나머지 변수들은 user1의 값을 복사합니다.

let user3 = User {
        email: String::from("another@example.com"),
        ..user1
    };

명심해야할게 user1을 이렇게 넘기면 user1의 username값이 소유권이 user3로 넘어가서 더이상 쓸수 없게됩니다. user1 인스턴스와 기본적으로 복사되는 변수들은 그대로 남아 있습니다.

traits

Debug trait

rect1:? 사용하면 바로 print로 데이터를확인가능. 😕 대신 :#? 을 사용하면 한줄대신 여러줄로 이쁘게 보여줌.

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    println!("rect1 is {rect1:?}");
}

method 멤버함수

위에서 만든 Rectangle 구조체의 면적구하는 함수를 멤버함수로 만들어 보겠습니다.

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    println!(
        "The area of the rectangle is {} square pixels.",
        rect1.area()
    );
}

associated functions = 생성자

이 생성자 메소드는 첫매개변수를 &self로 하지 않아도 된다.

impl Rectangle {
    fn square(size: u32) -> Self {
        Self {
            width: size,
            height: size,
        }
    }
}

댓글 남기기