DaleSchool

여러 값 다루기: 튜플, 배열, 벡터

입문15분

학습 목표

  • 튜플로 다른 타입의 값을 묶을 수 있다
  • 배열로 같은 타입의 고정 길이 값을 저장할 수 있다
  • Vec으로 크기가 변하는 목록을 만들 수 있다
  • for로 벡터의 요소를 순회할 수 있다

동작하는 코드

아래 코드를 Rust Playground에서 실행해보세요!

fn main() {
    // 튜플: 다른 타입의 값을 묶을 수 있어요
    let person = ("민수", 25, true);
    println!("이름: {}", person.0);
    println!("나이: {}", person.1);

    // 배열: 같은 타입, 고정 크기
    let colors = ["빨강", "초록", "파랑"];
    println!("첫 번째 색: {}", colors[0]);

    // 벡터: 같은 타입, 크기가 변할 수 있음
    let mut fruits = vec!["사과", "바나나", "포도"];
    fruits.push("딸기");
    println!("과일 수: {}", fruits.len());

    for fruit in &fruits {
        println!("🍎 {fruit}");
    }
}

세 가지 모두 여러 값을 하나로 묶는 방법이에요. 각각 쓰임새가 달라요!

직접 수정하기

  1. fruitspush로 좋아하는 과일을 2개 더 추가해보세요.
  2. colors[5]처럼 존재하지 않는 인덱스에 접근하면 어떻게 될까요? 직접 실행해보세요.

"왜?" — 세 가지 컬렉션의 차이

| | 튜플 (tuple) | 배열 (array) | 벡터 (Vec) | | --------- | ----------------- | ------------ | ----------------- | | 타입 | 서로 다른 타입 OK | 같은 타입만 | 같은 타입만 | | 크기 | 고정 | 고정 | 변경 가능 | | 만드는 법 | (값1, 값2) | [값1, 값2] | vec![값1, 값2] | | 접근 방법 | .0, .1 | [인덱스] | [인덱스] | | 추가/삭제 | 불가 | 불가 | push(), pop() |

언제 뭘 쓸지 헷갈리면 이렇게 생각하세요.

  • 튜플: 이름과 나이처럼 다른 종류의 값을 묶을 때
  • 배열: 크기가 절대 안 바뀌는 고정 목록 (요일, 색상 등)
  • 벡터: 나머지 거의 모든 경우! 실무에서 가장 많이 쓰여요
fn main() {
    let mut scores = vec![90, 85, 77, 92];

    // 요소 추가
    scores.push(88);

    // 마지막 요소 제거
    let last = scores.pop(); // Some(88)
    println!("꺼낸 값: {last:?}");

    // for로 순회
    for score in &scores {
        println!("점수: {score}");
    }

    println!("총 {}개의 점수", scores.len());
}

pop()의 결과가 Some(88)로 나오는 게 신기하죠? 이건 나중에 배울 Option 타입이에요. 지금은 "값이 있을 수도 없을 수도 있다"를 표현하는 거라고만 알아두세요.

심화 학습

구조 분해 — 튜플의 값을 한 번에 꺼내기

튜플에서 값을 꺼낼 때 .0, .1을 쓰는 것 말고도 구조 분해(destructuring) 라는 방법이 있어요.

fn main() {
    let person = ("민수", 25, true);

    // 구조 분해로 각 값에 이름 붙이기
    let (name, age, is_student) = person;
    println!("{name}님은 {age}살이에요");

    if is_student {
        println!("학생이에요!");
    }
}

.0, .1보다 훨씬 읽기 좋죠? 값에 의미 있는 이름을 붙일 수 있어서 코드가 깔끔해져요.

인덱스 범위 초과

배열이나 벡터에서 존재하지 않는 인덱스에 접근하면 프로그램이 패닉(panic) 을 일으켜요.

fn main() {
    let numbers = [1, 2, 3];
    println!("{}", numbers[10]); // 패닉!
}

에러 메시지:

thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 10'

해석: "배열 길이는 3인데 인덱스 10에 접근하려 했어요"라는 뜻이에요. 이건 컴파일 에러가 아니라 런타임 에러예요 — 프로그램이 실행되다가 멈추는 거예요. 인덱스가 범위 안에 있는지 항상 확인하세요!

Vec을 함수에 넘기면 생기는 일

fn print_scores(scores: Vec<i32>) {
    for s in scores {
        println!("{s}");
    }
}

fn main() {
    let scores = vec![90, 85, 77];
    print_scores(scores);
    println!("총 {}개의 점수", scores.len()); // 에러!
}

에러 메시지:

error[E0382]: borrow of moved value: `scores`

scores를 함수에 넘기고 나면 더 이상 사용할 수 없어요! 지금은 .clone()을 붙여서 복사본을 만들어 전달하면 돼요.

fn print_scores(scores: Vec<i32>) {
    for s in scores {
        println!("{s}");
    }
}

fn main() {
    let scores = vec![90, 85, 77];
    print_scores(scores.clone()); // 복사본을 전달!
    println!("총 {}개의 점수", scores.len()); // OK!
}

왜 이런 일이 생기는지는 나중에 자세히 알아볼게요. 지금은 .clone()이 복사본을 만든다고만 기억하세요!

  1. 좋아하는 음식 5개를 벡터에 저장하고, for로 번호와 함께 출력해보세요.
    • 힌트: 카운터 변수를 mut으로 만들어서 매 반복마다 1씩 증가시키세요.
    • 출력 예시: 1. 떡볶이, 2. 피자, ...
  2. 숫자 벡터 vec![10, 25, 30, 15, 40]에서 for로 순회하면서 30 이상인 값만 출력하는 프로그램을 작성해보세요.

Q1. 아래 코드에서 colors.len()의 값은 무엇일까요?

fn main() {
    let mut colors = vec!["빨강", "파랑"];
    colors.push("초록");
    colors.push("노랑");
    colors.pop();
    println!("{}", colors.len());
}
  • A) 2
  • B) 3
  • C) 4
  • D) 컴파일 에러

Q2. 튜플에서 두 번째 값에 접근하는 방법은?

let data = (100, "hello", true);
  • A) data[1]
  • B) data.1
  • C) data.get(1)
  • D) data(1)

Q3. 배열과 벡터의 가장 큰 차이는 무엇일까요?

  • A) 배열은 숫자만, 벡터는 문자열만 저장한다
  • B) 배열은 크기가 고정이고, 벡터는 크기를 바꿀 수 있다
  • C) 배열은 빠르고, 벡터는 느리다
  • D) 배열은 []로 접근하고, 벡터는 .get()으로만 접근한다