타나기
타나기 월드
타나기
전체 방문자
오늘
어제
  • 분류 전체보기 (90)
    • ⚙️ Rust (20)
      • 👀 Tutorial (20)
    • 🗿 Embedded (11)
      • 🐧 OS for ARM (11)
    • 💻 Study (37)
      • 🧩 알고리즘 (37)
    • 🏄🏽‍♂️ Life (21)
      • 🍚 타나구루망 (20)
      • 💡 Light (1)

인기 글

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
타나기

타나기 월드

⚙️ Rust/👀 Tutorial

[Rust / 튜토리얼] 11. 러스트에서 구조체를 정의하고 생성하기

2022. 5. 27. 00:21

구조체를 정의하고 초기화 하기

구조체는 앞에서 공부했던 튜플과 유사한 기능을 한다.
튜플처럼 각각의 요소들의 타입을 지정해 선언할 수 있다.
튜플과 다른 점은 타입과 함께 타입의 구성요소를 이름 지을 수 있다는 점이다.
이름을 지어주는 건 그 이름을 통해 데이터에 접근할 수 있다는걸 의미한다.
예를들어 타나기월드라는 구조체의 타나기 요소에 접근하고 싶다면, 타나기월드.타나기와 같은 식으로 접근할 수 있다.

구조체를 정의할 때는 struct키워드를 구조체 이름 앞에 적어주면 된다.
구조체의 이름을 짓는건 개발자 마음이지만 구조체 요소의 이름을 포괄하도록 짓는것이 좋다.
러스트에선 구조체의 요소들을 필드라고 부르고 있으니, 앞으로는 필드라고 부르도록 하겠다.

아래의 예시에서 구조체를 생성하고 인스턴스를 생성해 보자.

struct User {
    name: String,
    age: i32,
    alive: bool
}

fn main() {
    let tanagy = User {
        name: String::from("Tanagy"),
        age: 30,
        alive: true,
    };

    println!("tanagy {}", tanagy.name);
}
warning: `test_exam` (bin "test_exam") generated 2 warnings
    Finished dev [unoptimized + debuginfo] target(s) in 0.60s
     Running `target\debug\test_exam.exe`
tanagy Tanagy

User 라는 구조체를 정의하고 tanagy로 그 구조체를 인스턴스화 하는 예제이다.
예제에서 보이는 것 처럼 구조체를 정의할 때는 key:value 형식으로 선언 할 수 있다.
key는 해당 데이터에 접근하는 이름이고, value는 그 필드의 데이터이다.
그렇게 정의한 구조체를 사용하려면 인스턴스를 생성해야 하는데, 일반 변수를 생성 하는 것처럼 let키워드를 이용하면 된다.
그리고 구조체를 정의했던 것과 비슷하게 인스턴스를 생성하면 된다.
여기서 헷갈릴 수 있는 부분은 데이터를 할당할 때 =을 사용하지 않고 :를 사용했다는 것이다.
이 부분을 주의 하도록 하자.

인스턴스는 반드시 변경 가능 즉 mutable 해야 한다.
나이만 변경하고 싶어서 해당 필드만 mut 선언 할 수는 없다. 따라서 모든 필드가 변경될 수 있도록 let mut를 이용 해 구조체를 선언해 주자.

구조체도 다른 변수들과 마찬가지로, 표현식을 사용해 함수 마지막에 리턴할 수 있다.
User를 생성할 때 마다 alive 를 true로 해 주는 작업은 중복 작업이 될 것이다.
인스턴스화 하는 시점에서 user가 죽어있는 경우는 없을테니 말이다.
이걸 표현식을 이용해 타자 치는 작업을 줄여보자

struct User {
    name: String,
    age: i32,
    alive: bool
}

fn create_user(name:String, age:i32) -> User {
    User { name: name, age: age, alive: true }
}

fn main() {
    let tanagy = create_user("tanagy".to_string(), 30);
    println!("tanagy {}", tanagy.name);
}
    Finished dev [unoptimized + debuginfo] target(s) in 0.60s
     Running `target\debug\test_exam.exe`
tanagy tanagy

예시처럼 표현식을 이용하면 중복작업을 줄일 수 있고, 더욱 깔끔하게 구조체를 인스턴스화 할 수 있다.
그런데 잘 살펴 봐보자.
create_user()에서 name 과 age의 변수 이름이 같다.
이럴 경우 여기서 한번 더 간단하게 만들 수 있는 방법이 있다.

변수명이 필드명과 같을 때 필드를 초기화 하기

변수명과 구조체의 필드명이 같다면, 축약법을 사용할 수 있다.
축약법은 변수명 == 필드명일 경우 key:value쌍에서 value 를 생략할 수 있게 해 준다.

create_user()를 조금 수정해 보자.

fn create_user(name:String, age:i32) -> User {
    User { name, age, alive: true }
}

그러면 이렇게 간단한 함수로 나타낼 수 있다.
지금은 필드가 많지 않아서 그렇게 유용한지 못 느끼겠지만, 필드의 수가 많아질 수록 축약법은 더 가치있어질 것이다.

구조체 갱신법을 이용해 새 구조체 인스턴스 생성하기

User가 게임의 어느 한 캐릭터라고 생각해 보자.
그 캐릭터가 그림자 분신술을 배웠다.
그럼 분신의 수만큼 구조체를 인스턴스화 해야한다.
위에서 배운 내용만으로 인스턴스화 하다간 손가락이 부러질지도 모른다.
러스트는 이런 경우에 손가락을 보호해 줄 방법을 가지고 있다.
기존 값은 재활용 하고 바뀌는 필드만 재정의 하는 방법이다.

User의 name 필드만 새로 정의하고 나머지는 재활용 하는 코드를 구현해 보자

struct User {
    name: String,
    age: i32,
    alive: bool
}

fn create_user(name:String, age:i32) -> User {
    User { name, age, alive: true }
}

fn main() {
    let tanagy = create_user("tanagy".to_string(), 30);
    println!("tanagy {}", tanagy.name);
    let tanagy_clone1 = User {
        name: String::from("tanagy_clone1"),
        ..tanagy
    };
    println!("{} {}", tanagy_clone1.name, tanagy_clone1.age);
}
    Finished dev [unoptimized + debuginfo] target(s) in 0.57s
     Running `target\debug\test_exam.exe`
tanagy tanagy
tanagy_clone1 30

중간중간 println!()을 이용해 제대로 필드가 할당 되었는지 확인까지 해 보았다.
의도한 대로 재활용이 잘 된 것 같다.

구조체 데이터의 소유권

구조체에 데이터를 할당할 때 &str이 아닌 String을 사용했다.
아마 이전에 공부할 때처럼 습관적으로 사용했을수도 있지만, 이는 의도된 부분이다.
구조체는 구조체가 살아있는 동안 구조체의 필드 값들의 소유권을 가지고 있어야 하기 때문이다.

구조체가 소유권이 없는 데이터의 참조도 가지고 있을 수 있지만, 이건 뒤에 나올 라이프 타임을 사용할 때를 전제로 한다.
이 라이프 타임은 구조체가 존재하는 동안참조하는 데이터를 계속 존재할 수 있게 해준다.
만약 라이프 타임을 사용하지 않고 참조만 가지고 있게 된다면 다음과 같은 에러가 발생한다.

struct User {
    name: &str,
    age: i32,
    alive: bool
}

fn main() {
    let tanagy = User {
        name: "tanagy",
        age: 30,
        alive: true,
    };
}
   Compiling test_exam v0.1.0 (C:\Folder\source\rust\test_exam)
error[E0106]: missing lifetime specifier
 --> src\main.rs:2:11
  |
2 |     name: &str,
  |           ^ expected named lifetime parameter
  |
help: consider introducing a named lifetime parameter
  |
1 ~ struct User<'a> {
2 ~     name: &'a str,
  |

For more information about this error, try `rustc --explain E0106`.
error: could not compile `test_exam` due to previous error
터미널 프로세스 "C:\Users\zingr\.cargo\bin\cargo.exe 'run'"이(가) 종료되었습니다(종료 코드: 101).

자세한 해석은 뒤의 장에서 더 알아보자!

저작자표시 (새창열림)

'⚙️ Rust > 👀 Tutorial' 카테고리의 다른 글

[Rust / 튜토리얼] 13. 러스트의 메소드.  (0) 2022.06.01
[Rust / 튜토리얼] 12. 러스트 구조체를 이용한 예제 프로그래밍  (0) 2022.05.29
[Rust / 튜토리얼] 10. 러스트의 슬라이스 (slice)  (0) 2022.05.25
[Rust / 튜토리얼] 9. 러스트의 참조자와 빌림  (0) 2022.05.23
[Rust / 튜토리얼] 8. 러스트의 소유권  (0) 2022.05.20
    '⚙️ Rust/👀 Tutorial' 카테고리의 다른 글
    • [Rust / 튜토리얼] 13. 러스트의 메소드.
    • [Rust / 튜토리얼] 12. 러스트 구조체를 이용한 예제 프로그래밍
    • [Rust / 튜토리얼] 10. 러스트의 슬라이스 (slice)
    • [Rust / 튜토리얼] 9. 러스트의 참조자와 빌림
    타나기
    타나기
    #include<all>

    티스토리툴바