벡터(Vec\<T\>)로 같은 타입의 값 목록 저장하기

벡터(Vec<T>)로 같은 타입의 값 목록 저장하기

여러 개의 값을 한곳에 모아 두고 싶을 때 가장 먼저 떠올리는 자료 구조가 벡터예요. Rust의 벡터는 Vec<T> 타입으로, 같은 타입의 값들을 메모리에 연속으로 배치해 저장해요. 그래서 다른 타입은 한 벡터에 섞어 담을 수 없다는 점을 미리 알고 가야 해요.

출처: The Rust Programming Language - Storing Lists of Values with Vectors

새 벡터 만들기

빈 벡터를 만들려면 Vec::new()를 호출해요. 다만 값을 아직 넣지 않았기 때문에 Rust는 요소 타입을 추론할 수 없어서, 타입 주석을 붙여 줘야 해요.

let v: Vec<i32> = Vec::new();

하지만 실제로는 초기값을 넣어 만드는 경우가 훨씬 흔해요. vec! 매크로를 쓰면 값을 나열하는 것만으로도 벡터를 만들 수 있고, 타입도 값으로부터 자동 추론돼요.

let v = vec![1, 2, 3];

요소를 추가하려면 push 메서드를 써요. 값을 바꿀 수 있게 하려면 mut으로 선언해야 해요.

let mut v = Vec::new();
v.push(5);
v.push(6);
v.push(7);

벡터 요소 읽기

요소를 꺼낼 때는 인덱스나 get 메서드를 써요. get은 요소가 없을 때 패닉 대신 Option을 돌려주기 때문에, 사용자가 잘못된 숫자를 입력하는 상황처럼 요소가 없을 수 있는 경우에 더 안전해요.

let v = vec![1, 2, 3];
let third = v.get(2);      // Some(&3)
let none = v.get(100);     // None

인덱스로 벡터를 순회할 수도 있지만, for 루프로 요소 하나하나를 돌며 읽는 게 더 자연스러워요. 이때 변경 작업을 섞으면 대여 검사기(borrow checker)가 컴파일 에러를 내줘요.

for i in &v {
    println!("{i}");
}

값을 바꾸면서 돌려면 가변 참조와 역참조 연산자 *를 써요.

let mut v = vec![100, 32, 57];
for i in &mut v {
    *i += 50;
}

enum으로 서로 다른 타입 저장하기

벡터는 같은 타입만 담을 수 있어서, 서로 다른 타입을 한 목록에 넣고 싶으면 enum을 쓰는 게 정석이에요. enum의 variant는 모두 같은 enum 타입 아래 묶이기 때문에, 필요한 타입들을 variant로 정의하면 하나의 벡터에 담을 수 있어요.

enum SpreadsheetCell {
    Int(i32),
    Float(f64),
    Text(String),
}

let row = vec![
    SpreadsheetCell::Int(3),
    SpreadsheetCell::Text(String::from("blue")),
    SpreadsheetCell::Float(10.12),
];

벡터는 스코프를 벗어나면 해제되면서 담고 있던 모든 요소도 함께 정리돼요. Vec<T>는 일반 struct처럼 스코프 끝에서 자동으로 드롭(drop)되고, 그 안의 값들도 차례로 정리돼요. 벡터의 요소를 가리키는 참조는 반드시 벡터가 살아있는 동안에만 써야 한다는 점도 대여 규칙을 통해 보장받을 수 있어요.

더 알아보기