C++은 구조체도 있고, 클래스도 따로있습니다. 요즘은 구조체도 멤버함수가있고 클래스의 은닉성만 갖고있지 않는걸로 알고있는데, Rust에서는 class가 따로 존재하지 않습니다.
먼저 C++과 Rust를 비교해보면 Rust는 아래 표와같이 C++기능을 대응하고있습니다.
C++은 구조체와 클래스를 구분해놓고 클래스가 좀더 많은 기능을 지원하는 정도의 차이만 있습니다. Rust의 구조체는 C++의 구조체와 기본기능이 더 가깝다고 할수있지만 추가 키워드를 활용해 C++에서 할수있는 기능들을 대응하고있습니다. Rust 공식문서에서는 상속구조대신 합성구조를 선택한이유가 메모리 절약을 위해서라고 합니다. 아마도 상속구조가 복잡해지면, 자식 클래스에서 안쓰능기능이나 멤버변수가 점점 늘어나는것을 지적하는거같네요. 참고로 파이썬유저가 있으시다면, 파이썬의 클래스는 C++과 더많이 닮아 있습니다.
| Rust | C++ |
| struct | struct |
| 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,
}
}
}