
Intro
Rust는 Class가 없습니다. 대신에 Rust는 다음과 같은 개념으로 Object에 대한 정의를 합니다.
- Data의 구조를 설계하는 Struct (구조)
- Behavior(행동)를 정의하는 Trait (특성)
- Data에 Behavior를 연결하여 구현하는 Impl (구현)
Rust에서는 Struct, Trait, Impl 이 3가지로 Object를 정의합니다. 개인적인 사용 경험에 따르면 Struct + Impl로 구현을 하거나 mod + Struct + Impl + Trait으로 이전 class처럼 만드는 것도 가능합니다. 하지만 Rust의 철학은 확실히 class가 아닌 3가지로 분리한 문법을 지향하고 있습니다. 이유는 아래와 같습니다.
- 명시적이고 유연한 설계 : 데이터와 동작을 분리하여 같은 데이터에 다른 동작을 부여할 수 있음.
- 상속보다는 조합 : 기존의 상속 개념은 필요 이상의 계층 구조를 만들기 쉽고, 다이아몬드 문제, 강한 결합 등 문제를 일으킨다. 상속 대신 조합으로 구조를 단순화.
- 다형성의 안전성과 제약 : trait은 정적 및 동적 디스패치를 지원하며, trait bound로 제네릭 타입 제약을 명확히 정의해 다형성을 안전하고 효율적으로 활용할 수 있음.
- Ownership System과의 통합 : 기존의 Class는 소유권 시스템과 융합되기 어려움.
- 클래스 기반 언어는 보통 객체의 상태를 가변 참조로 공유하는 패턴을 많이 사용하지만, Rust는 소유권과 참조자 안전성(ownership, borrowing)을 강제하여 데이터 경쟁(data race)을 방지함.
- struct와 impl을 사용하면 소유권과 수명을 명시적으로 설계할 수 있어 메모리 안전성을 높일 수 있음. - 단순하고 명확한 모델 : class는 데이터와 메서드를 모두 포함하며 암묵적으로 동작하는 경우가 많지만, Rust는 구조(struct)와 동작(trait), 구현(impl)을 명확히 분리하여 코드의 의도가 분명히 드러남.
막상 저도 이런 부분에 대해서 장점인 부분보다는 새로운 문법...을 익히는데 힘을 쏟다보니 장점이 아직 막 와닿지는 않습니다. 실제로 배포할 정도의 Application을 만들어야 감이 올 듯 합니다.
그럼 Struct, Impl, Trait 순서대로 예제를 보도록 하겠습니다.
Code Example (Struct)
Rust 1.79.0 (released 2024-06-13)
fn main(){
/* Struct : type들의 집합. Tuple과 비슷하다. */
// Tuple vs Struct
// Tuple은 index로 하위 type에 접근 가능
// Struct는 보다 명시적으로 keyword를 통해 하위 type에 접근 가능
/* Struct 선언 예제 (key, value) */
#[derive(Debug)]
struct User {
user_name: String,
email: String,
active: bool,
sign_in_count: u64,
}
// 정의된 struct로 변수 생성
let mut user1 = User{
user_name: String::from("bob"),
email: String::from("example@gmail.com"),
active: true,
sign_in_count: 0,
};
// print struct : #[derive(Debug)]가 있어야 struct를 {:?}를 활용해 사용할 수 있음.
println!("{user1:?}");
// 하위 Type에 접근
println!("{}", user1.user_name); // bob
println!("{}", user1.email); // example@gmail.com
println!("{}", user1.active); // true
println!("{}", user1.sign_in_count); // 0
// 하위 Type 값 변경
user1.user_name.push_str(" add name");
user1.sign_in_count += 1;
println!("{}", user1.user_name); // bob add name
println!("{}", user1.sign_in_count); // 1
// Shorthand struct initialization를 활용하여 짧은 문법으로 struct 변수 생성
fn gen_user(user_name:String, email:String) -> User {
// 만약 변수명과 struct의 keyword가 같다면 이렇게 key,value를 일일이 작성할 필요 없다.
User {
user_name: user_name,
email: email,
active: false,
sign_in_count: 0,
}
}
let user2 = gen_user(
String::from("Aaron"),
String::from("example@gmail.com")
);
// struct로 선언된 변수를 활용해 새로운 변수 만들기
let _user3 = User {
email: String::from("another@gmail.com"),
..user2 // 마지막에 추가. ".." 용법에 대해서는 차후에 자세히 설명
};
/* Struct 선언 예제 (type) */
// type으로만 선언한 경우 tuple과 마찬가지로 index로 접근
struct Color(u8, u8, u8);
let red = Color(255, 0, 0);
println!("r, g, b : {}, {}, {}", red.0, red.1, red.2);
/* Struct 선언 예제 (빈 struct) */
// flag나 특정 상태를 의미적으로 나타낼 때 사용. 여기서는 간단히 소개만 함.
#[derive(Debug)]
struct Marker;
let marker = Marker;
println!("{:?}", marker); // Marker
}
Code Example (Impl)
fn main() {
/* Struct + impl 예제 */
// Struct 정의
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
// Rectangle에 관련된 메서드를 정의
impl Rectangle {
// 메서드 정의: &self는 메서드 호출 시 인스턴스를 참조로 받음
fn area(&self) -> u32 {
self.width * self.height
}
// 메서드 정의: &self가 아닌 값을 직접 리턴
fn is_square(&self) -> bool {
self.width == self.height
}
// 메서드 정의: &mut self는 메서드 호출 시 인스턴스를 가변 참조로 받음
fn scale(&mut self, factor: u32) {
self.width *= factor;
self.height *= factor;
}
// 연관 함수 정의: Self 타입 반환 (구조체 생성자 역할)
fn new(width: u32, height: u32) -> Self {
Self { width, height }
}
}
// Rectangle 인스턴스 생성
let mut rect1 = Rectangle::new(30, 50);
// struct 출력 (Debug)
println!("{:?}", rect1); // Rectangle { width: 30, height: 50 }
// 메서드 호출
println!("Area: {}", rect1.area()); // Area: 1500
println!("Is square: {}", rect1.is_square()); // Is square: false
// 크기 변경
rect1.scale(2);
println!("{:?}", rect1); // Rectangle { width: 60, height: 100 }
// 새 Rectangle 생성
let rect2 = Rectangle::new(40, 40);
println!("Is square: {}", rect2.is_square()); // Is square: true
/* 다른 struct + impl 예제 */
// 구조체 정의
struct Point {
x: f64,
y: f64,
}
impl Point {
// 두 점 사이의 거리를 계산하는 메서드
fn distance(&self, other: &Point) -> f64 {
let dx = self.x - other.x;
let dy = self.y - other.y;
(dx * dx + dy * dy).sqrt()
}
// 원점(0, 0)을 반환하는 연관 함수
fn origin() -> Self {
Self { x: 0.0, y: 0.0 }
}
}
let point1 = Point { x: 3.0, y: 4.0 };
let point2 = Point::origin();
println!(
"Distance between point1 and origin: {}",
point1.distance(&point2)
); // Distance between point1 and origin: 5
}
Code Example (Trait)
fn main() {
/* Trait + impl + Struct 예제 */
// Trait 정의: 공통 동작을 추상화
trait Describe {
fn describe(&self) -> String; // 객체 정보를 문자열로 반환하는 메서드
}
// Struct 정의: Book
struct Book {
title: String,
author: String,
}
// Book에 대한 Describe 트레이트 구현
impl Describe for Book {
fn describe(&self) -> String {
format!("Book: '{}' by {}", self.title, self.author)
}
}
// Struct 정의: Car
struct Car {
brand: String,
model: String,
}
// Car에 대한 Describe 트레이트 구현
impl Describe for Car {
fn describe(&self) -> String {
format!("Car: {} {}", self.brand, self.model)
}
}
// Struct 정의: Animal
struct Animal {
species: String,
name: String,
}
// Animal에 대한 Describe 트레이트 구현
impl Describe for Animal {
fn describe(&self) -> String {
format!("Animal: A {} named {}", self.species, self.name)
}
}
// 여러 struct를 같은 Trait으로 처리하는 함수.
// impl trait을 input으로 받는다는 것 : trait을 struct 기반으로 impl한 구현체를 input으로 받음
// 즉 Describe trait을 impl한 것을 input으로 허용한다는 것.
fn print_description(item: &impl Describe) {
println!("{}", item.describe());
}
// Example 실행
let book = Book {
title: String::from("The Rust Programming Language"),
author: String::from("Steve Klabnik and Carol Nichols"),
};
let car = Car {
brand: String::from("Tesla"),
model: String::from("Model S"),
};
let animal = Animal {
species: String::from("Dog"),
name: String::from("Buddy"),
};
// 각 구조체의 describe 호출
print_description(&book); // Book: 'The Rust Programming Language' by Steve Klabnik and Carol Nichols
print_description(&car); // Car: Tesla Model S
print_description(&animal); // Animal: A Dog named Buddy
}
* reference
'Rust > Basic' 카테고리의 다른 글
[Rust Tutorial] 10 - Generic (0) | 2024.11.25 |
---|---|
[Rust Tutorial] 9 - mod, use (2) | 2024.11.20 |
[Rust Tutorial] 7 - Ownership (0) | 2024.11.17 |
[Rust Tutorial] 6 - Control Flow (0) | 2024.11.17 |
[Rust Tutorial] 5 - Function (0) | 2024.11.17 |