- 컴파운드 타입(compound type)은 여러 개의 값을 하나의 타입으로 그룹화한 타입
- 러스트에선 두 가지의 컴파운드 타입을 지원
- 튜플(tuples)
- 배열(arrays)
변수의 타입 확인하기
- 컴파운드 타입에 대해 알아보기 전에, 컴파운드 타입이 어떻게 이루어졌는지 잘 살펴보기 위해 변수의 타입을 출력하는 함수를 만들어 놓자. (학습용 및 디버깅 목적)
// main.rs
/** 변수의 타입을 출력하는 함수 */
fn print_type_of<T>(_: &T) {
println!("{}", std::any::type_name::<T>());
}
*참고: https://stackoverflow.com/questions/21747136/how-do-i-print-the-type-of-a-variable
튜플 타입
- 고정된 길이를 가지며 크기를 변경할 수 없다.
- 생성 방법
- 소괄호 안에 각 값을 쉼표로 구분하여 표기 (타입 같지 않아도 됨.)
fn main() {
let tup1 = (910, 33.3, 9);
// 다음과 같이 type annotation을 적용하는 것도 가능
let tup2: (i32, f64, u8) = (910, 33.3, 9);
print_type_of(&tup1); // (i32, f64, i32)
print_type_of(&tup2); // (i32, f64, u8)
}
- 저번 시간에 공부했던 내용과 같이, 러스트에서 기본 정수 타입은 i32이기 때문에 정수의 타입을 따로 명시하지 않으면 컴파일러는 해당 정수의 타입을 i32로 추론한다. 따라서 원하는 타입이 있다면 tup2를 정의할 때와 같이 type annotation을 사용하면 된다.
- 다음과 같이 튜플을 구조 분해하여 튜플의 개별 값을 읽을 수도 있다.
let tup2: (i32, f64, u8) = (910, 33.3, 9);
let (_, a, b) = tup2;
println!("a + b = {}", a + f64::from(b)); // a + b = 42.3
- 또한 다음과 같이 마침표 다음에 원하는 요소의 인덱스를 사용하여 해당 요소를 직접 참조할 수도 있다. (보통의 경우와 마찬가지로 튜플의 첫 인덱스도 0부터 시작)
let nine1 = tup1.2;
let nine2 = tup2.2;
print_type_of(&nine1); // i32
print_type_of(&nine2); // u8
- 근데 여기서 흥미로운 건... nine1과 nine2를 비교하는 문을 적는 순간, 타입을 명시하지 않았던 nine1의 타입이 기본 정수 타입인 i32에서 u8로 바뀐다는 것이다.
print_type_of(&nine1); // u8
print_type_of(&nine2); // u8
println!("nine1 == nine2 일까요? 정답은 {} 입니다.", nine1 == nine2);
// nine1 == nine2 일까요? 정답은 true 입니다.
배열 타입
- 튜플과 달리 배열의 모든 요소는 동일한 타입이어야 한다.
- 튜플과 마찬가지로 배열도 고정된 길이를 가진다.
- 생성 방법
- 대괄호 안에 각 값을 쉼표로 구분하여 나열 (타입 모두 동일해야 함.)
let arr = ["토끼", "다람쥐", "참새"];
print_type_of(&arr); // [&str; 3]
- 배열은 데이터를 heap 메모리가 아닌 stack 메모리에 할당하거나, 고정된 개수의 요소를 다룰 때 유용하다고 한다.
- 배열은 stack에 할당된 한 덩어리의 메모리
- cf.) 벡터(Vector)
- 표준 라이브러리가 지원함.
- 배열과 유사하나 더 유연함. (크기를 자유롭게 조정할 수 있음.)
- 보통은 벡터를 사용하는 편이 더 유용하긴 하나, 길이가 늘 고정되어 있는 데이터를 다루고자 할 때는 배열이 더 적합함. (예: 일 년에 포함된 월의 이름)
// type annotation을 사용하고자 할 때는 다음과 같이 표기하면 된다.
let months: [&str; 12] = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
println!("일 년에는 몇 개의 월이 존재하나요?: {}개", months.len());
// 일 년에는 몇 개의 월이 존재하나요?: 12개
- 배열 초기화하는 방법 (모두 동일한 값으로) : let 변수명 = [요소; 길이];
// 배열 초기화하기
let many_cats = ['🐱'; 20];
print_type_of(&many_cats); // [char; 20]
println!("{}마리의 고양이", many_cats.len()); // 20마리의 고양이
- 배열에 접근하는 방법 : 배열[인덱스]
let cat = many_cats[0];
println!("고양이: {}", cat); // 고양이: 🐱
// 튜플처럼 마침표 뒤에 인덱스를 표기하는 방식은 배열에 대해선 사용할 수 없다.
// let cat = many_cats.0;
// ⛔️instead of using tuple indexing, use array indexing: `many_cats[0]`
- 배열 범위를 벗어난 인덱스의 값을 읽으려고 하면, 컴파일 시점이 아닌 런타임에 에러가 발생한다!
- 이렇게 범위 밖의 인덱스 값을 읽으려는 시도에 에러를 발생시키는 것은 러스트의 안전성 원리(memory safety principles)와 관련 있다. 보통 저수준 언어에선 이러한 시도에 엉뚱한 값을 읽어오기도 하지만 러스트는 프로그램을 중단시켜 그러한 위험을 방지한다.
let no_cat = many_cats[20];
println!("고양이...?: {}", no_cat);
// ⛔️런타임 에러! 🙅🏻♀️
// thread 'main' panicked at 'index out of bounds: the len is 20 but the index is 20', src/main.rs:61:18
// note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
참고
https://rinthel.github.io/rust-lang-book-ko/ch03-00-common-programming-concepts.html
'학습 내용 > Rust' 카테고리의 다른 글
[일반 프로그래밍 개념] 데이터 타입 - 1. 스칼라 타입 (0) | 2022.05.24 |
---|---|
[일반 프로그래밍 개념] 변수 (0) | 2022.05.23 |
macOS에서 rust-analyzer 세팅 (0) | 2022.05.22 |
[숫자 맞히기 게임 만들기] 정답 맞힐 때까지 다중 입력 지원하기 + 올바르지 않은 입력 처리하기 (0) | 2022.03.09 |
[숫자 맞히기 게임 만들기] 난수와 사용자 입력 비교하기 (0) | 2022.02.15 |