println!(“{name} is {age}”, name=”Alice”, age=30);
“Alice is 30”
<
왼쪽 정렬
{:<10}
“left “
>
오른쪽 정렬
{:>10}
” right”
^
가운데 정렬
{:^10}
” center “
fill
채움 문자 변경
{:*^10}”
“***hi****”
width
폭 지정
{:8}”
” 42″
width$
변수 폭 지정
{:width$}”
폭이 변수값
+
항상 부호 표시. 양수도 +, 음수는 – ※ 기본은 음수만 – 표시하고, 양수는 미표시
{:+}”
+42
‘ ‘(공백)
(열 맞추기 위해) 양수일 때 공백 한 칸 추가
println!(“{: }”, 42);
” 42″
#
진번 접두사 표시
#{:#x}”
“0x2a”
0
제로 패딩(0으로 채움)
println!(“{:08.2}”, 3.14159);
“00003.14” (폭 8, 소수점 2자리)
.precision
소수점 자리수
{:.2}
“3.14”
3. 천 단위 구분 쉼표
Rust 표준 라이브러리에는 없으므로 외부 크레이트 또는 수동 구현이 필요합니다.
가. num-format (다국어 지원)
[dependencies] num-format = "0.4"
use num_format::{Locale, ToFormattedString};
fn main() { let n = 123456789; println!("{}", n.to_formatted_string(&Locale::en)); // 123,456,789 println!("{}", n.to_formatted_string(&Locale::de)); // 123.456.789 }
나. separators (경량)
[dependencies] separators = "0.4"
use separators::Separatable;
fn main() { let n = 123456789; println!("{}", n.separated_string()); // 123,456,789 }
다. 직접 구현
fn format_with_commas(n: i64) -> String { let s = n.to_string(); let mut chars: Vec<char> = s.chars().rev().collect(); let mut result = String::new(); for (i, c) in chars.iter().enumerate() { if i != 0 && i % 3 == 0 { result.push(','); } result.push(*c); } result.chars().rev().collect() }
Rust에서 array와 vector는 모두 여러 개의 값을 순차적으로 저장하는 자료구조입니다. 하지만 두 타입은 메모리 관리, 크기, 사용 목적 등에서 중요한 차이점이 있습니다. 이 글에서는 각 자료구조의 특징, 사용법, 예제, 그리고 언제 어떤 것을 선택해야 하는지에 대해 자세히 알아보겠습니다.
let slice = &v[1..3]; println!(“{:?}”, slice); => v 벡터에서 1번 인덱스부터 3미만인 2번 인덱스까지 참조 형식으로 가져오는 slice는 [99, 3]이 됩니다. Vector는 일반 포맷인 {}로는 출력이 안되므로 디버그 포맷인 {:?}으로 출력해야 합니다.
let doubled: Vec<i32> = v.iter().map(|x| x * 2).collect(); println!(“{:?}”, doubled); => v 벡터의 요소 들에 2를 곱한 후 새로운 벡터로 변환한 후 doubled에 저장합니다. doubled의 타입도 i32타입의 Vector이므로 디버그 포맷으로 출력하면 [2, 198, 6, 8, 10, 12, 14, 16, 18]가 출력됩니다.
6. 성능 차이의 실제 사례
6.1. 반복문에서의 성능
fn main() { let arr = [1; 1000000]; let vec = vec![1; 1000000];
let mut sum = 0; for i in 0..arr.len() { sum += arr[i]; }
let mut sum2 = 0; for i in 0..vec.len() { sum2 += vec[i]; } }
실행 시간: 배열이 벡터보다 약간 더 빠른 경우가 많습니다.
이유: 배열은 스택에 연속적으로 저장되어 CPU 캐시 효율이 높고, 컴파일러가 최적화를 더 적극적으로 적용할 수 있습니다.
위와 같이 배열의 개수를 1백만개로 숫자가 1인데도 array의 경우 overflow가 발생하므로 1십만으로 바꾸는데, 시간을 체크하는 부분을 추가하고, 천단위 쉼표를 추가하도록 아래와 같이 수정한 후
[Cargo.toml] -num-format 크레이트를 사용하기 위해 필요
[dependencies] num-format = "0.4"
[main.rs]
use std::time::Instant; use num_format::{Locale, ToFormattedString};
fn main() { let arr = [1; 100_000]; let vec = vec![1; 100_000];
// 배열 합계 시간 측정 let start = Instant::now(); let mut sum = 0; for i in 0..arr.len() { sum += arr[i]; } let duration = start.elapsed(); println!("Array sum: {}, elapsed: {:?}", sum.to_formatted_string(&Locale::ko), duration);
// 벡터 합계 시간 측정 let start = Instant::now(); let mut sum2 = 0; for i in 0..vec.len() { sum2 += vec[i]; } let duration = start.elapsed(); println!("Vector sum: {}, elapsed: {:?}", sum2.to_formatted_string(&Locale::ko), duration); }
Instant::now()로 현재 시각을 기록하고, 반복문이 끝난 후 elapsed()로 소요 시간을 구합니다.
for i in 0..arr.len()라고 0부터 배열의 길이전까지 i를 반복하면 sum에 arr[i]를 더하도록 했는데, for i in arr.iter()라고 하고, sum += i;이라고 해도 되는데, iter()를 이용한 것이 훨씬 빠릅니다. 특히 Vector 속도가 많이 빨라졌습니다. Array sum: 100,000, elapsed: 728.6µs Vector sum: 100,000, elapsed: 875.7µs ※ 그런데 매번 속도가 다르기 때문에 위 수치가 절대적인 것은 아닙니다. 어느 때는 Vector가 빠른 경우도 있습니다.
sum 또는 sum2 다음에 num_format의 ToFormattedString(&Locale::ko)를 추가해서 숫자에 천단위마다 쉼표를 추가합니다.
6.2. 크기 변경 및 데이터 추가
배열은 크기가 고정되어 있어, 데이터 추가/삭제가 불가능합니다.
벡터는 push, pop, extend 등으로 동적으로 크기를 조절할 수 있지만, 이 과정에서 메모리 재할당이 발생할 수 있습니다. 대량의 데이터를 추가할 때는 재할당 오버헤드가 성능 저하 요인이 됩니다.
6.3. 벤치마크 및 실제 사용 조언
고정 크기, 빠른 반복/접근이 필요하다면 배열이 유리합니다.
크기가 가변적이거나, 데이터 추가/삭제가 빈번하다면 벡터가 적합합니다.
대용량 데이터 처리에서 벡터는 힙 할당 및 재할당 비용이 있으므로, 성능이 민감한 경우 벡터의 용량을 미리 예약(with_capacity)하는 것이 좋습니다.
※with_capacity란?
Vec::with_capacity는 Rust의 벡터(Vec)를 생성할 때 초기 용량(capacity) 을 미리 지정하는 메서드입니다.
with_capacity(n)은 최소 n개의 요소를 저장할 수 있는 공간을 미리 할당한 빈 벡터를 생성합니다.
이렇게 하면, 벡터에 요소를 추가할 때마다 메모리를 재할당하는 비용을 줄일 수 있어 성능이 향상됩니다.