Rust에서 Box를 사용하여 옵션형, 알려진 길이의 배열 메모리 할당 최적화하기

2024-07-27

배경

Rust는 메모리 안전성을 위해 컴파일 타임에 메모리 할당을 검사합니다. 이는 대부분의 경우 유리하지만, 옵션형(optional) 또는 알려진 길이(known length)의 배열을 다룰 때 불필요한 메모리 할당과 복사가 발생할 수 있습니다.

Box 사용하기

Box는 Rust에서 스마트 포인터 역할을 하는 자료구조입니다. Box를 사용하면 메모리 할당을 직접 관리하지 않고도 힙(heap) 메모리에 데이터를 저장할 수 있습니다.

예시

다음 예시는 Box를 사용하여 옵션형 배열을 메모리 할당하는 방법을 보여줍니다.

// Option<T>
let mut array: Option<Box<[i32]>> = None;

// 값 할당
if some_condition {
  array = Some(Box::new([1, 2, 3]));
}

// 값 사용
if let Some(array) = array {
  for element in array.iter() {
    println!("{}", element);
  }
}

이 예시에서 array는 옵션형 변수이며, Box::new()를 사용하여 힙 메모리에 배열을 할당합니다. array에 값이 할당된 경우, iter() 메서드를 사용하여 배열 요소를 반복할 수 있습니다.

장점

  • 옵션형 배열의 경우, Box를 사용하면 불필요한 메모리 할당과 복사를 줄일 수 있습니다.
  • 알려진 길이의 배열의 경우, Box를 사용하면 컴파일 타임에 배열 크기를 확인할 수 있어 메모리 안전성을 향상시킬 수 있습니다.

단점

  • Box를 사용하면 메모리 관리가 더 복잡해질 수 있습니다.
  • Box는 가비지 컬렉션(garbage collection) 대상이므로, 메모리 누수(memory leak)를 방지하기 위해 주의해야 합니다.

결론




예제 코드

옵션형 배열

// Option<T>
let mut array: Option<Box<[i32]>> = None;

// 값 할당
if some_condition {
  array = Some(Box::new([1, 2, 3]));
}

// 값 사용
if let Some(array) = array {
  for element in array.iter() {
    println!("{}", element);
  }
}

알려진 길이의 배열

// [T; n]
let array: Box<[i32]> = Box::new([1, 2, 3]);

// 값 사용
for element in array.iter() {
  println!("{}", element);
}

옵션형 배열

알려진 길이의 배열




Box 대체 방법

옵션형 배열

  • Vec::with_capacity 메서드를 사용하여 배열 용량을 미리 할당할 수 있습니다.
  • MaybeUninit 자료구조를 사용하여 초기화되지 않은 배열을 만들 수 있습니다.

알려진 길이의 배열

  • [T; n] 형식을 사용하여 알려진 길이의 배열을 직접 선언할 수 있습니다.
  • std::array::from_fn 함수를 사용하여 배열 요소를 직접 생성할 수 있습니다.

옵션형 배열 예시

// Vec::with_capacity
let mut array: Vec<i32> = Vec::with_capacity(3);

// 값 할당
if some_condition {
  array.push(1);
  array.push(2);
  array.push(3);
}

// 값 사용
for element in array.iter() {
  println!("{}", element);
}
// MaybeUninit
use std::mem::MaybeUninit;

let mut array: MaybeUninit<[i32; 3]> = MaybeUninit::uninit();

// 값 할당
if some_condition {
  unsafe {
    let mut array = array.assume_init();
    array[0] = 1;
    array[1] = 2;
    array[2] = 3;
  }
}

// 값 사용
if let Some(array) = unsafe { array.assume_init() } {
  for element in array.iter() {
    println!("{}", element);
  }
}

알려진 길이의 배열 예시

// [T; n]
let array: [i32; 3] = [1, 2, 3];

// 값 사용
for element in array.iter() {
  println!("{}", element);
}
// std::array::from_fn
use std::array;

let array: [i32; 3] = array::from_fn(|i| i + 1);

// 값 사용
for element in array.iter() {
  println!("{}", element);
}

rust serde

rust serde

Rust에서 tap() 함수를 사용하여 반복자를 어떻게 활용할 수 있을까요?

tap() 함수의 활용 예시:로그 출력:위 코드는 numbers 배열의 각 요소를 반복하면서, tap() 함수를 사용하여 각 요소를 콘솔에 출력합니다. tap() 함수는 반복자를 변형하지 않기 때문에, collect() 함수를 통해 원본 배열을 그대로 벡터로 변환할 수 있습니다