홈강좌Rust 입문·중급 47편 중 22편째
Rust 구조체 — 관련된 값을 이름 붙여 묶는다
Rust 구조체(struct)는 관련된 값 여러 개를 칸마다 이름을 붙여 하나로 묶은 타입이다. struct 로 정의하고, 만들 때는 모든 칸을 채워야 한다. 하나라도 빠지면 E0063 이 난다. #[derive(Debug)] 를 붙이면 {:?} 로 통째로 찍을 수 있다.
01 struct 로 정의하고 칸 이름으로 읽는다
회원 한 명을 이름·나이·활동 여부로 묶어 보자. 8편의 튜플로도 묶을 수 있지만, 튜플은 .0·.1 번호라 무엇이 무엇인지 알기 어렵다. 구조체는 칸마다 이름이 있다.
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);
} 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: truestruct Member { … }는 새 타입Member를 정의한다. 중괄호 안에칸이름: 타입을 쉼표로 늘어놓는다. 타입 이름은Member처럼 대문자로 시작한다.- 만들 때는
Member { name: …, age: …, active: … }처럼 칸마다 값을 넣는다. 순서는 정의와 달라도 된다. - 읽을 때는
ferris.name처럼 점 뒤에 칸 이름을 쓴다.
02 칸을 빠뜨리면 E0063
active 칸을 빼고 만들어 보자.
struct Member {
name: String,
age: u8,
active: bool,
}
fn main() {
let ferris = Member {
name: String::from("Ferris"),
age: 9,
};
println!("{}", ferris.name);
}error[E0063]: missing field `active` in initializer of `Member`
--> src/main.rs:8:18
|
8 | let ferris = Member {- 무엇 —
Member를 만드는 자리(initializer)에active칸이 빠졌다. - 어디 — 8번째 줄의
Member. - 고치는 법 —
help:는 없지만 무엇 줄이 빠진 칸 이름을 댄다.active: true,를 넣는다.
Rust 에는 "값이 없는 칸" 이 없다. 다른 언어처럼 빠진 칸이 null 이나 undefined 로 채워지지 않는다. 값이 없을 수도 있는 칸은 26편의 Option 으로 따로 표시한다.
03 칸을 고치려면 변수 전체가 mut
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);
} Compiling member v0.1.0 (~/projects/member)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.19s
Running `target/debug/member`
Ferris is now 10let mut ferris 로 만들면 ferris.age = … 처럼 칸을 고칠 수 있다. 칸 하나만 mut 로 할 수는 없다. 구조체 전체가 바뀔 수 있거나, 전체가 못 바뀌거나 둘 중 하나다.
04 실습 — 만드는 함수와 {:?}
회원을 만드는 함수를 두고, 결과를 통째로 찍어 보자.
#[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:#?}");
} 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 처럼 알려 준다. 다음 편에서는 구조체에 딸린 함수, 메서드를 만든다.