티스토리 뷰
반응형
Struct 선언 및 이용
Struct 란?
- 속성값들을 묶어서 관리한다. JAVA 의 클래스와 유사함.
struct User { // 구조체 정의
name: String,
email: String,
active: bool,
}
fn main() {
let user = User { // 구조체 인스턴스 생성
name: String::from("홍길동"),
email: String::from("gildong@email.com"),
active: true,
};
println!("이용자의 이름은 = {}", user.name); // 인스턴스 값 출력
}
구조체 인스턴스의 값을 변경하고 싶은 경우 인스턴스 생성시에 mut 를 붙여줘야한다.
fn main() {
let mut user = User { // 인스턴스 생성
name: String::from("홍길동"),
email: String::from("gildong@email.com"),
active: true,
};
user.name = String::from("길동 홍");
println!("이용자의 이름은 = {}", user.name); // 인스턴스 값 출력
}
build_user() 같은 유틸리티 함수를 이용해서 초기값을 정의할 수 있다. build_user() 는 active 속성이 항상 true 로 생성된다.
struct User { // 구조체 정의
name: String,
email: String,
active: bool,
}
fn build_user(name: String, email: String) -> User {
User {
name: name,
email: email,
active: true,
}
}
fn build_user(name: String, email: String) -> User {
User {
name, // 대입하고자 하는 필드와 파라미터 이름이 같으면 생략가능.
email,
active: true,
}
}
fn main() {
let user = build_user(String::from("홍길동"), String::from("gildong@eamil.com"));
println!("이용자의 이름은 = {}", user.name); // 인스턴스 값 출력
}
이미 있는 구조체 인스터로부터 새로운 인스턴스를 생성하는 경우, 소유권 이전이 일어난다.
아래 예제는 user1으로 부터 user2 를 생성했다. user1의 name, email 소유권은 이미 user2 로 넘어간 상태이기 때문에 user1 은 더 이상 사용할 수 없다.
fn main() {
let user1 = build_user(String::from("홍길동"), String::from("gildong@eamil.com"));
let user2: User = User {
name: user1.name,
email: user1.email,
active: false,
};
/*
// .. 을 이용해서 기존 인스턴스 내용을 복사해올 수 있다.
let user2 = User {
active: false,
..user1
};
*/
println!("user2.email = {}", user2.email);
println!("user1.email = {}", user1.email); // ERROR! user1 의 이메일은 user2로 소유권이 넘어갔다!
}
튜플 구조체
- 일반적인 튜플 사용법과 동일하지만, 튜플에 이름을 붙임으로서 타입을 보다 엄격하게 관리할 수 있다.
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
fn tuple_struct() {
let color = Color(255, 255, 255);
let point = Point(0, 0, 0);
color.0;
color.1;
}
화면에 구조체를 예쁘게 출력하는 방법
- 구조체 위에 #[derive(Debug)] 를 붙이고, “{:?}” 를 이용해 구조체 인스턴스를 출력한다.
- #[derive(Debug)]: Debug 를 위한 뭔가를 만들어줘! (이것을 Derived Trait 이라고 부른다)
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect = Rectangle {
width: 20,
height: 30,
};
println!("해당 사각형의 면적은 {}.", area(&rect));
println!("사각형 = {:?}", rect); // {} = std::display, {:?} = std::debug
}
fn area(rect: &Rectangle) -> u32 {
rect.width * rect.height
}
- db!() 를 이용한다.
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect = Rectangle {
width: 20,
height: 30,
};
println!("해당 사각형의 면적은 {}.", area(&rect));
dbg!(rect);
}
fn area(rect: &Rectangle) -> u32 {
rect.width * rect.height
}
메소드 Method
- fn 과 사용법이 비슷하나, Enum, Trait, Struct 안에서 정의하고 사용한다.
- 파라미터를 소유권임대 없이 사용하게되면 (&self 가 아니라 그냥 self : Self 로 쓰면) main() 에서 rect.area() 를 호출한 이후에 rect 를 사용할 수 없게된다. (소유권이 area 로 넘어가버리기 때문)
- 즉, 메소드 파라미터는 반드시 참조 방식으로 선언 해줘야한다.
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle { // Rectangle 구조체 안에 정의하는 Method
fn area(&self) -> u32 { //&self 는 self: &Self 를 축약해서 쓴 표현이다. self: &Rectangle
self.width * self.height
}
}
fn main() {
let rect = Rectangle {
width: 20,
height: 30,
};
println!("이 사각형의 면적은 {}입니다.", rect.area());
}
하나의 구조체에 여러개의 impl 을 작성할 수 있다.
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area ...
}
impl Rectangle {
fn strange_calc(self: &mut Self) -> u32 { // 동일하게 &mut 를 붙이면 내부 속성을 변경할 수 있다.
self.width = 10;
self.width * self.height
}
}
fn main() {
let mut rect = Rectangle { // 속성을 변경할 수 있게 mut 을 붙여줌.
width: 20,
height: 30,
};
println!("이 사각형의 면적 이상 계산은 {}입니다.", rect.strange_calc());
}
연관함수
- impl 블록 내에서 정의된 모든 함수는 해당 impl 의 이름이 붙은 유형과 연관되어 있기 때문에 연관함수라고 부른다.
- self 를 첫번째 매개변수로 가지고 있지 않은 연관함수를 정의할 수 있는데(따라서 메서드가 아님), 이는 작업을 수행하기 위해 해당 유형의 인스턴스가 필요하지 않기 때문이다.
- 함수의 소유권을 이전하지 않고 파라미터를 정의한 후, 자기 자신을 반환하는 함수
- static 생성자 개념이라고 보면 될 것 같다.
- 관용적으로 new 로 많이 만들지만 String::from() 처럼 from() 으로 만들기도 하고 아래처럼 square와 같이 이름지을 수도 있다.
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(self: &Self) -> u32 {
self.width * self.height
}
}
impl Rectangle {
fn square(size: u32) -> Self { //Self 를 Rectangle 이라고 바꿔도 문제없음.
Self {
width: size,
height: size,
}
}
}
fn main() {
let rect = Rectangle {
width: 20,
height: 30,
};
println!("이 사각형의 면적은 {}입니다.", rect.area());
println!("정사각형 = {:?}", Rectangle::square(20));
}
Enum
#[derive(Debug)]
enum Color {
Red,
Green,
Blue,
}
fn main() {
let red = Color::Red;
let green = Color::Green;
println!("red = {:?}", red); //red = Red
}
와 같이 다양한 방식으로 Enum 을 사용할 수 있다.
enum Message {
StartGame,
WinPoint { who: String },
ChangePlayerName(String),
}
fn message() {
let message = Message::StartGame;
let message2 = Message::WinPoint {
who: String::from("길동"),
};
let message3 = Message::ChangePlayerName(String::from("둘리"));
}
Rust에는 Option 이란 기본 내장 Enum 클래스가 정의돼 있다.
Rust 에는 NULL 개념이 없고, 이 Option 타입의 하나인 None 인지 아닌지로 값의 유무를 결정한다.
// enum Option<T> {
// None,
// Some(T),
// }
fn some_values() {
let some_number = Some(3);
let absent_number: Option<i32> = None;
let x: i32 = 2;
// x + some_number; // x는 정수타입이고 some_number 는 Option 타입이라 계산이 안된다.
}
Enum 타입에 특정 기능을 부여하고 싶다면 match 를 이용하면 된다.
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => {
println!("Lucky penny!");
1
} // 중괄호 뒤에는 , 를 붙이지 않아도 된다. 하지만 붙여도 잘 돌아가니 붙이는게 좋지 않을까.
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
Some(i) => Some(i + 1),
None => None, // 요렇게도 매칭할 수 있다.
}
}
fn main() {
let x = Some(5);
println!("{:?}", plus_one(x)); // Option 에는 default 출력형식이 없어서 :? 로 출력
println!("{:?}", plus_one(None));
}
/*
Some(6)
None
*/
match 는 선언한 모든 Enum 에 대해 정의해줘야 하지만 _라는 와일드 카드를 이용해서 간편하게 작성할 수 도 있다.
enum Message {
StartGame,
WinPoint { who: String },
ChangePlayerName(String),
}
/* 패턴매치 기능으로 Enum 값에 따른 처리를 하기 좋습니다. */
fn handle_message(message: &Message) {
match message {
Message::StartGame => println!("게임시작!"),
Message::WinPoint { who } => println!("{}의 득점", who),
Message::ChangePlayerName(_) => println!("플레이어 이름변경 요청"),
}
}
fn handle_message2(message: &Message) {
match message {
Message::StartGame => println!("게임시작!"),
_ => println!("아직 정의하지 못한 메세지 입니다."),
}
}
fn main() {
let message = Message::WinPoint{who: String::from("홍길동")};
handle_message2(&message); // 아직 정의하지 못한 메세지 입니다.
}
반응형