[1. 개요]
연관된 여러 값들을 묶어서 의미있는 데이터 단위로 정의한다.
연관함수 정의 방법을 정리한다.
[2. 구조체 정의 및 생성]
struct User {
sign_in_count: u64,
active: bool,
}
fn main() {
let user1 = User {
active: true,
sign_in_count: 1,
};
let user2 = user1;
println!("{}", user2.sign_in_count);
println!("{}", user1.sign_in_count); // 구조체 user1 은 Copy trait 을 구현하지 않아서
// 소유권이 이동하여, 컴파일에러가 발생한다.
}
구조체는 mut 를 통해 멤버 변경이 가능하나,
특정 필드만 변경할 수 있도록 허용하진 않는다.
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
fn build_user(email: String, username: String) -> User {
User {
email, // 구조체 필드와 변수 이름이 같은 경우, 필드 생략이 가능하다.
username,
active: true,
sign_in_count: 1,
}
}
위와 같이 인스턴스 생성 시 구조체 필드와 변수 이름이 같은 경우,
필드 생략이 가능하다.
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
fn main() {
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
let user2 = User {
email: user1.email,
username: String::from("anotherusername567"),
active: user1.active,
sign_in_count: user1.sign_in_count,
};
let user3 = User {
email: String::from("another@example.com"),
username: String::from("anotherusername789"),
..user2 // 구조체 갱신법
};
println!{"{}", user1.email}; // email 은 string type 이므로, 값이 이동되어 컴파일 에러가 발생한다.
println!("{}, {}, {}", user1.username, user2.username, user3.username); // 값을 복사하여, 소유권 문제가 없다.
}
구조체간의 대입연산은 소유권 문제가 있으나,
구조체 멤버 간 대입은 스택에 저장되는 데이터는 소유권 문제 없이 복사된다.
구조체 갱신법은 입력으로 주어진 인스턴스와 변화하지 않는 필드들을 명시적으로 할당하지 않기 위해
.. 구문을 사용한다.
=> 일일히 다 입력하지 않아도 된다.
=> comma 는 없어야 한다.
=> 변하는 값을 먼저 작성하고 사용해야 한다.
[3. 튜플 구조체]
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
튜플의 타입은 같지만, 구조체 명이 다르므로,
black 과 origin 은 서로 다른 타입이다.
=> 두 변수 간 소유권을 이동 할 수 없다.
[4. 유사 유닛 구조체]
어떤 필드도 없는 구조체 정의가 가능하다.
이는 유닛 타입인 () 와 비슷하게 동작하여, 유사 유닛 구조체라 부른다.
이 구조체는 특정한 타입의 trait 을 구현해야하지만,
타입 자체에 데이터를 저장하지 않는 경우에 유용하다.
[5. 데이터 소유권]
struct User {
username: &'static str, // 참조자의 Lifetime 을 명시하지 않으면 컴파일 에러가 발생한다.
email: &'static str,
sign_in_count: u64,
active: bool,
}
fn main() {
let user1 = User {
email: "someone@example.com",
username: "someusername123",
active: true,
sign_in_count: 1,
};
println!("{}, {}", user1.username, user1.email);
}
[6. 파생 트레잇]
println! 매크로는 다양한 종류의 포맷을 출력할 수 있으며,
기본적으로 {}은 println!에게 Display라고 알려진 포맷팅을 이용하라고 전달한다.
그러나 구조체를 사용하는 경우, println!이 출력을 형식화하는 방법은 덜 명확하여, 컴파일 에러가 발생한다.
그래서 {} 내 :? (또는 {:#?} ) 를 삽입하여, println! 에게 Debug 라는 출력 포맷을 사용할 것을 명시한다.
#[derive(Debug)]
struct Rectangle {
length: u32,
width: u32,
}
fn main() {
let rect1 = Rectangle { length: 50, width: 30 };
println!("rect1 is {:?}", rect1);
}
Rust 는 derive 어노테이션을 이용한 여러 트렛잇을 제공한다.
[7. 메소드 정의]
#[derive(Debug)]
struct Rectangle {
length: u32,
width: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.length * self.width
}
fn update(&mut self) {
self.length += 1;
self.width += 1;
}
fn shrink(self) -> u32 {
self.width + self.length
}
}
fn main() {
let rect1 = Rectangle { length: 50, width: 30 };
rect1.update(); // rect1 이 mut 가 아니므로, 컴파일 에러가 발생한다.
println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
);
rect1.shrink(); // 호출 시 rect1 의 소유권이 이동하므로, 아래 코드의 컴파일 에러가 발생한다.
println!("{:?}", rect1);
}
Rust 는 C++ 에서의 -> 연산자와 동치인 연산자를 갖지 않으며,
자동 참조 및 역참조 라는 기능을 갖고 있다.
이는 어떤 object의 메소드를 호출 했을 때,
Rust 가 자동으로 & 나 &mut 혹은 * 를 붙여서 object 가 해당 메소드의 시그니처와 맞도록 한다.
그리고 이것은 메소드가 명확한 수신자, self 의 type 을 갖고 있기 때문에 동작한다.
[8. 연관 함수]
impl 블록 내 self 파라미터를 갖지 않는 함수
=> (C++ 기준, class 내 static 와 비슷?)
연관 함수는 새로운 구조체의 인스턴스를 반환해주는 생성자로서 자주 사용된다.
struct Rectangle {
length: u32,
width: u32,
}
impl Rectangle {
fn square(size: u32) -> Rectangle {
Rectangle { length: size, width: size }
}
}
let sq = Rectanble::square(3);
'Rust' 카테고리의 다른 글
Rust mod, pub, use (0) | 2022.08.24 |
---|---|
Rust 열거형과 패턴 매칭 (0) | 2022.08.20 |
슬라이스 (0) | 2022.08.18 |
참조자와 빌림 (0) | 2022.08.18 |
소유권 (0) | 2022.08.17 |