본문 바로가기
개발/러스트 (Rust)

Rust 기본 : 벡터 (Vector)

by 레조 2024. 8. 26.

https://rustacean.net/

 

Rust 기본 : 벡터 (Vector)

 

러스트에서 제공하는 Vec<T>타입은 동적 배열이다.

 - 동적으로 크기 조정 가능하며 Vec<T>는 T 타입으로 고정되어 타입 안전하다.

 - 0부터 시작하는 인덱스로 접근 가능하다. ex) v[0]

 - 반복자(iterator)와 for 루프를 통해 각 요소에 접근할 수 있다.

 

Vector 생성 방법 1 : Vec::new()

벡터는 동일한 타입 요소를 가지는 동적 배열이다. 그러나 배열과 달리 힙에 할당된다.

fn main() {

    // 빈 벡터 생성
    let v: Vec<String> = Vec::new();
    let mut v = Vec::new();
    
    // 생성한 스트링 소유권은 vector로 이동한다.
    // -> vector가 삭제될 때 내부 모든 스트링 소유권도 연쇄 삭제된다.
    v.push(String::from("One"));
    v.push(String::from("Two"));
    v.push(String::from("Three"));
    
} // vector dropped.

 

Vector 생성 방법 2 : 매크로 vec!

fn main() {
    let v2 = vec![1, 2, 3];
}

 

생성한 벡터에 접근 방법 1 : &v[0]

fn main() {
    // 생성 방법 2
    let v2 = vec![1, 2, 3];

    //let s = v[0]; // 백터 스트링 소유권을 밖으로 이동할 수 없다.
    let s = &v[0]; // 그러나 참조(borrowing)는 인덱스를 초과하면 panic 발생.
    
    // 안전하게 소유권을 이전하려면 인덱스를 remove 한다.
    // 모든 요소의 인덱스가 앞으로 당겨진다.
    let s = v.remove(0);
}

 

생성한 벡터에 접근 방법 2 : v.get(0)

fn main() {
    // 생성 방법 2
    let v2 = vec![1, 2, 3];
   
    // 안전하다!
    let s = v.get(0);
    if let Some(e) = s {
        println!("{e}");
    }
    
    // 깔끔하게 안전하다!
    if let Some(e) = v.get(0) {
        println!("{e}");
    }
}

 

생성한 벡터에 접근 방법 3 : iterator

fn main() {

    let v = vec![
        String::from("One"),
        String::from("Two"),
        String::from("Three")
    ];    

    // &mut v : ownership은 그대로 두고 스트링 수정.
    for s in &mut v {
        s.push_str("!!");
    }

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

 

벡터 요소 값으로 옮기기

fn main() {

    let v = vec![
        String::from("One"),
        String::from("Two"),
        String::from("Three")
    ];

    let mut v3 = vec![];
    
    // v 백터 데이터를 v3 백터로 소유권 이동(ownership move)
    // v의 syntax sugar는 v.into_iter(), 별로 달지 않은데... 이런 게 러스트 맛?
    for s in v.into_iter() { // v == v.into_iter()
        v3.push(s);
    }
    
    // v의 모든 요소 소유권은 이동하였다. 더이상 접근은 유효하지 않다.
    //let i = v.get(0); // borrow of moved value
}

 

소유권 이동 없이 복사하려면 clone() 한다.

fn main() {

    let v = vec![
        String::from("One"),
        String::from("Two"),
        String::from("Three")
    ];

    let mut v3 = vec![];
    
    for s in (&v).into_iter() {
        v3.push(s.clone());
    }
    
    // clone() 하였기 때문에 접근 가능하다.
    let i = v.get(0);
}

 

 

위 예제들은,

소유권 이동 관계를 섬세하게 이해하기 좋다.

아래 코드는 어떤 에러가 나는가?

fn main() {
    let mut v = vec![
        String::from("One"),
        String::from("Two"),
        String::from("Three")
    ];

    let mut v3 = vec![];
    
    for s in (&v).into_iter() { // immutable borrowing
        v3.push(s);
    }
    
    v.remove(0); // error : mutable borrowing
    let i = v3.get(0); // v3의 0번 인덱스는 유효하지 않은 값일 텐데 사전에 error 처리!
}

 

remove 메소드는 스스로를 가변 참조 한다.

for 루프에서 &v로 불변 참조 값을 전달했는데 v.remove()로 가변 참조하여 요소를 삭제 시도하면서 러스트 소유권 규칙을 위반하였다.

 

러스트 소유권 규칙.

 - 각 값은 해당 값을 소유하는 변수를 가진다.

 - 값은 한 번에 하나를 가변 참조 하거나 여러 개의 불변 참조를 가질 수 있다. (둘다 적용 불가!)

 - 참조는 항상 유효해야한다.