hyunsu blog

홈강좌Rust 입문·중급 47편 중 22편째

Rust 구조체 — 관련된 값을 이름 붙여 묶는다

목차 · 4
  1. 01struct 로 정의하고 칸 이름으로 읽는다
  2. 02칸을 빠뜨리면 E0063
  3. 03칸을 고치려면 변수 전체가 mut
  4. 04실습 — 만드는 함수와 {:?}

Rust 구조체(struct)는 관련된 값 여러 개를 칸마다 이름을 붙여 하나로 묶은 타입이다. struct 로 정의하고, 만들 때는 모든 칸을 채워야 한다. 하나라도 빠지면 E0063 이 난다. #[derive(Debug)] 를 붙이면 {:?} 로 통째로 찍을 수 있다.

01 struct 로 정의하고 칸 이름으로 읽는다

회원 한 명을 이름·나이·활동 여부로 묶어 보자. 8편의 튜플로도 묶을 수 있지만, 튜플은 .0·.1 번호라 무엇이 무엇인지 알기 어렵다. 구조체는 칸마다 이름이 있다.

rust
struct Member {
    name: String,
    age: u8,
    active: bool,
}

fn main() {
    let ferris = Member {
        name: String::from("Ferris"),
        age: 9,
        active: true,
    };

    println!("{} is {}", ferris.name, ferris.age);
    println!("active: {}", ferris.active);
}
text
   Compiling member v0.1.0 (~/projects/member)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.28s
     Running `target/debug/member`
Ferris is 9
active: true
  • struct Member { … } 는 새 타입 Member 를 정의한다. 중괄호 안에 칸이름: 타입 을 쉼표로 늘어놓는다. 타입 이름은 Member 처럼 대문자로 시작한다.
  • 만들 때는 Member { name: …, age: …, active: … } 처럼 칸마다 값을 넣는다. 순서는 정의와 달라도 된다.
  • 읽을 때는 ferris.name 처럼 점 뒤에 칸 이름을 쓴다.

02 칸을 빠뜨리면 E0063

active 칸을 빼고 만들어 보자.

rust
struct Member {
    name: String,
    age: u8,
    active: bool,
}

fn main() {
    let ferris = Member {
        name: String::from("Ferris"),
        age: 9,
    };
    println!("{}", ferris.name);
}
text
error[E0063]: missing field `active` in initializer of `Member`
 --> src/main.rs:8:18
  |
8 |     let ferris = Member {
  1. 무엇 — Member 를 만드는 자리(initializer)에 active 칸이 빠졌다.
  2. 어디 — 8번째 줄의 Member.
  3. 고치는 법 — help: 는 없지만 무엇 줄이 빠진 칸 이름을 댄다. active: true, 를 넣는다.

Rust 에는 "값이 없는 칸" 이 없다. 다른 언어처럼 빠진 칸이 null 이나 undefined 로 채워지지 않는다. 값이 없을 수도 있는 칸은 26편의 Option 으로 따로 표시한다.

03 칸을 고치려면 변수 전체가 mut

rust
struct Member {
    name: String,
    age: u8,
}

fn main() {
    let mut ferris = Member {
        name: String::from("Ferris"),
        age: 9,
    };
    ferris.age = ferris.age + 1;
    println!("{} is now {}", ferris.name, ferris.age);
}
text
   Compiling member v0.1.0 (~/projects/member)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.19s
     Running `target/debug/member`
Ferris is now 10

let mut ferris 로 만들면 ferris.age = … 처럼 칸을 고칠 수 있다. 칸 하나만 mut 로 할 수는 없다. 구조체 전체가 바뀔 수 있거나, 전체가 못 바뀌거나 둘 중 하나다.

04 실습 — 만드는 함수와 {:?}

회원을 만드는 함수를 두고, 결과를 통째로 찍어 보자.

rust
#[derive(Debug)]
struct Member {
    name: String,
    age: u8,
}

fn new_member(name: &str, age: u8) -> Member {
    Member {
        name: name.to_string(),
        age,
    }
}

fn main() {
    let ferris = new_member("Ferris", 9);
    println!("{} is {}", ferris.name, ferris.age);
    println!("{ferris:?}");
    println!("{ferris:#?}");
}
text
   Compiling member v0.1.0 (~/projects/member)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.19s
     Running `target/debug/member`
Ferris is 9
Member { name: "Ferris", age: 9 }
Member {
    name: "Ferris",
    age: 9,
}
  • new_member 는 Member 를 돌려주는 함수다. 마지막 식이 Member { … } 라 그 값이 반환값이다(11편).
  • age, 는 age: age, 를 줄여 쓴 것이다. 칸 이름과 변수 이름이 같으면 한 번만 쓴다.
  • 맨 위의 #[derive(Debug)] 는 "{:?} 로 찍는 기능을 저절로 만들어 달라" 는 표시다. 이것이 없으면 {ferris:?} 가 컴파일되지 않는다. 9편의 배열은 처음부터 이 기능을 갖고 있었다.
  • {:#?} 는 칸마다 줄을 바꿔 보기 좋게 찍는다. 칸이 많을 때 쓴다.

Member 에 email: String 칸을 더해 보자. 고칠 곳이 정의·new_member·찍는 줄 셋이다. 하나라도 빠뜨리면 컴파일러가 이 편의 E0063 처럼 알려 준다. 다음 편에서는 구조체에 딸린 함수, 메서드를 만든다.