[Rust] 데이터 타입(2) - 복합 타입

RedBean·2024년 7월 21일
0

Rust 배우기

목록 보기
3/4
post-thumbnail

복합 타입

복합 타입은 여러 값을 하나의 타입으로 묶을 수 있다.
러스트에는 튜플(tuple)과 배열(array), 두 가지 기본 복합 타입이 있다.

튜플 타입

튜플은 다양한 타입의 여러 값을 묶어 하나의 복합 타입으로 만드는 일반적인 방법이다. 튜플은 고정된 길이를 가진다. 즉, 한 번 선언되면 크기를 늘리거나 줄일 수 없다.

괄호 안에 쉼표로 값들의 목록을 작성하면 튜플을 만들 수 있다. 각 위치는 타입을 가지고, 튜플 내의 타입들은 서로 달라도 된다. 굳이 타입을 명시하지 않아도 되지만, 타입을 명시해본 예제이다.

fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
}

튜플로부터 개별 값을 받아오려면 아래와 같이 튜플 값을 해체해 사용할 수 있다.

fn main() {
    let tup = (500, 6.4, 1);

    let (x, y, z) = tup;

    println!("The value of y is: {y}");
}

이렇게 해체하는 것을 구조 해체라고 부른다.
이렇게 하지 않고도, 마침표(.)뒤에 접근하고자 하는 값의 인덱스를 쓰는 방식으로도 튜플 요소에 접근할 수 있다.

fn main() {
    let x: (i32, f64, u8) = (500, 6.4, 1);

    let five_hundred = x.0;

    let six_point_four = x.1;

    let one = x.2;
}

튜플 중, 아무 값도 없는 튜플은 유닛(unit)이라는 특별한 이름을 가진다. 이 값과 타입은 모두 ()로 작성되고, 빈 값이나 빈 반환 타입을 나타낸다.
표현식이 어떤 값도 반환하지 않는다면 암묵적으로 유닛 값을 반환한다.

배열 타입

여러 값의 집합체를 만드는 다른 방법으로는 배열이 있다. 튜플과는 다르게 배열의 모든 요소는 서로 같은 타입이여야 한다. 다른 언어들과는 다르게 러스트의 배열은 고정된 길이를 가진다.

대괄호 안에 쉼표로 구분해 값을 나열하면 만들 수 있다.

fn main() {
    let a = [1, 2, 3, 4, 5];
}

힙보다는 스택에 데이터를 할당하고 싶을 때나(힙과 스택은 이후 다룰 것이다) 항상 고정된 개수의 요소로 이루어진 경우라면 배열이 유용하다.

let months = ["January", "February", "March", "April", "May", "June", "July",
              "August", "September", "October", "November", "December"];

아래와 같이 대괄호 안에 요소의 타입과 요소의 개수를 세미콜론으로 구분해 적는 방식으로 배열의 타입을 작성할 수도 있다.

또한 다음과 같이 대괄호 안에 초깃값과 세미콜론을 쓴 다음 배열의 길이를 적는 방식을 사용하여 모든 요소가 동일한 값으로 채워진 배열을 초기화할 수도 있다.

무슨 말인지 이해가 되지 않는다면 아래의 코드를 예시로 들어보자.

let a = [3; 5];

여기서 a는 모두 3으로 채워진 5개의 요소를 가지게 될 것이다. let a = [3, 3, 3, 3, 3]보다 편리한 방식이다.

배열 요소에 접근하기

아래와 같이 인덱스를 통해 배열 요소에 접근할 수 있다.

fn main() {
    let a = [1, 2, 3, 4, 5];

    let first = a[0];
    let second = a[1];
}

이 예제에서 first로 이름 붙여진 변수는 배열에서 인덱스[0]의 값이므로 1이 될 것이다. 마찬가지로 second 변수는 배열의 [1] 인덱스로부터 얻어진 값 2가 될 것이다.

유효하지 않은 배열 요소에 대한 접근

만약 배열의 끝을 넘어선 요소에 접근하려고 하면 어떤 일이 벌어질까?

아래 코드를 바탕으로 설명하겠다.

use std::io;

fn main() {
    let a = [1, 2, 3, 4, 5];

    println!("Please enter an array index.");

    let mut index = String::new();

    io::stdin()
        .read_line(&mut index)
        .expect("Failed to read line");

    let index: usize = index
        .trim()
        .parse()
        .expect("Index entered was not a number");

    let element = a[index];

    println!("The value of the element at index {index} is: {element}");
}

이 코드는 정상적으로 컴파일된다. 코드를 실행하고 0, 1, 2, 3, 4를 입력한다면 프로그램은 그 인덱스에 해당하는 배열 값을 출력할 것이다.

그런데 만약, 우리가 여기에 배열의 끝을 넘어서는 10같은 숫자를 입력한다면?

러스트는 다음과 같이 출력한다.

thread 'main' panicked at 'index out of bounds: the len is 5 but the index is 10', src/main.rs:19:19
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

만약 인덱스가 배열 크기보다 크거나 같을 경우 러스트는 패닉을 일으킨다. 특히 위의 경우 이러한 검사는 런타임에서 이루어지는데, 이는 사용자가 코드를 실행하고 무슨 값을 넣을지 컴파일러는 전혀 모르기 때문이다.

다음 글에서는 함수에 대해 다루겠다.

profile
Rust를 배우고 있는

0개의 댓글

관련 채용 정보