동작하는 코드
아래 코드를 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}");
}
}
세 가지 모두 여러 값을 하나로 묶는 방법이에요. 각각 쓰임새가 달라요!
직접 수정하기
fruits에push로 좋아하는 과일을 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()이 복사본을 만든다고만 기억하세요!
- 좋아하는 음식 5개를 벡터에 저장하고,
for로 번호와 함께 출력해보세요.- 힌트: 카운터 변수를
mut으로 만들어서 매 반복마다 1씩 증가시키세요. - 출력 예시:
1. 떡볶이,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()으로만 접근한다