Rust Formatting Cheat Sheet

formatting trait와 placeholder, formatter 기본 옵션에 대해 알아보고, 천단위 구분 표시(나라별 차이)에 대해서도 추가로 알아보겠습니다.

1. formatting trait과 placeholder

traitplaceholder 예설명
Display
{}사용자 친화적 출력
Debug{:?}/{:#?}개발·디버그 출력 / Pretty print
Binary{:b}2진수
Octal{:o}8진수
LowerHex{:x}16진수(소문자)
UpperHex{:X}16진수(대문자)
Pointer{:p}포인터 주소
LowerExp{:e}지수 표기법(소문자 e)
UpeperExp{:E}{:E}지수 표기법(대문자 E)

2. Formatter 옵션 (기본)

{[arg]:[fill][align][sign][#][0][width][.precision][type]}
옵션의미예시결과
{}자동 인덱스println!(“{} {}”, 1, 2); “1 2”
{0}(1}위치 인덱스println!(“{0} {1} {0}”, “a”, “b”);“a b a”
{name}이름(키) 인자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()
}

fn main() {
println!("{}", format_with_commas(123456789)); // 123,456,789
}

4. 예시 모음

fn main() {
let n = 42;
println!("{:<10}", n); // 왼쪽 정렬
println!("{:>10}", n); // 오른쪽 정렬
println!("{:^10}", n); // 가운데 정렬
println!("{:*^10}", n); // 가운데 정렬 + * 채움
println!("{:08}", n); // 0으로 채움
println!("{:+}", n); // +42
println!("{:#x}", n); // 0x2a
println!("{:b}", n); // 101010
println!("{:width$}", n, width = 6); // 변수 폭 지정
}

fmt::Display trait

Debug는 구조체 정보를 그대로 출력하고, fmt::Display는 원하는 방식으로 표현을 커스터마이징할 수 있게 해줍니다. Debug는 #[derive(Debug)]로 자동 구현이 되고, fmt::Display는 impl fmt::Display를 통해 수동 구현이 필요합니다.

구분DebugDisplay
목적디버깅용 출력사용자 친화적 출력
매크로println!(“{:?}”, x)println!(“{}”, x)
구현 방법#[derive(Debug)]로 자동 구현수동 구현 필요 (impl fmt::Display)
포맷 예시Matrix(1,0, 2.0, 3.0, 4.0)(1.0, 2.0)
(3.0, 4.0)
문자열화format!(“{:?}”, x)x.to_string() 또는
format!(“{}”, x)

1. tuple 구조체

가. 선언

#[derive(Debug)]
struct Matrix(f32, f32, f32, f32);
  • [derive(Debug)]는 Rust에서 자동으로 Debug 트레이트를 구현해 달라는 뜻입니다. 이를 통해 해당 구조체(또는 열거형 등)를 {:?} 형식으로 출력할 수 있게 됩니다.
  • Matrix 구조체는 f32 타입의 값 4개를 가지며, 필드에 이름이 없어서 튜플처럼 순서로 접근 합니다.

나. instance 생성 및 Debug 포맷 출력

fn main() {
    let matrix = Matrix(1.1, 1.2, 2.1, 2.2);
    println!("{:?}", matrix);
}
  • Matrix라는 튜플 구조체의 새 인스턴스(instance)를 생성하는 코드입니다.
  • (1.1, 1.2, 2.1, 2.2) 라는 4개의 f32 값을 순서대로 Matrix 구조체에 넣어 새 변수 matrix에 저장합니다.
  • println!(“{:?}”, matrix);는 Debug 트레잇을 이용해 구조체 내부 값을 출력하는 것입니다.
    출력값은 Matrix(1.1, 1.2, 2.1, 2.2)입니다.

2. fmt::Display 트레잇 구현

위는 Debug 트레잇으로 출력하는 것이고, fmt::Display 트레잇을 통해 다양한 포맷으로 출력할 수 있습니다.

use std::fmt;

impl fmt::Display for Matrix {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // 원하는 포맷으로 출력 구성
        write!(
            f,
            "( {} {} )\n( {} {} )",
            self.0,self.1, self.2,self.3
        )
    }
}
  • use std::fmt;
    => Rust 표준 라이브러리(std) 안에 있는 fmt 모듈을 현재 스코프(scope)로 가져온다는 뜻입니다.
    fmt는 그 안에 문자열 포맷(formatting) 관련 함수와 트레이트들이 모여있는 모듈(module)입니다.
    use std::fmt;를 하면, 나중에 fmt::Display나 fmt::Formatter 같은 타입이나 트레이트를 쓸 때, std::fmt 전체를 계속 적지 않고 그냥 fmt만 써도 됩니다.

  • impl fmt::Display for Matrix
    => Matrix에 대해 Display 트레이트를 수동으로 구현합니다.
  • fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    => 출력 형식을 어떻게 할지를 정의하는 함수입니다.
    메소드명은 fmt이고, 인수로 자기 자신인 &self와 가변 참조 형식의 fmt::Fomatter 형식(구조체)의 f를 받고, 반환 형식은 fmt::Result입니다.

    fmt::Result는 Result<(), fmt::Error>의 별칭으로,
    성공하면 Ok(()), 실패하면 Err(fmt::Error)를 반환합니다.
  • write! 매크로의 입력 포맷은 write!(f, “{}”, value)입니다. “{}”라는 포맷에 value를 넣고, 그 결과를 f에 기록합니다.
  • “( {} {} )\n( {} {} )”는 괄호안에 값 2개씩을 넣어 출력하는데, \n은 줄 바꿈을 의미합니다.
  • self.0,self.1, self.2,self.3는 포맷에 들어갈 값을 순서대로 나열한 것입니다.

3. main 함수

fn main() {
    let matrix = Matrix(1.1, 1.2, 2.1, 2.2);

    // Debug 포맷
    println!("{:?}", matrix);

    // Display 포맷
    println!("{}", matrix);
}

위에서 첫번째 println! 매크로는 Debug 트레잇으로 출력하는 것으로 위에서 봤고,

두번째 println! 매크로는 fmt::Display 트레잇으로 출력하는 것으로 2번에서 구현한 바 있습니다.

출력하면 아래와 같이 튜플 구조체의 값이 두 개씩 괄호안에 싸여 두 줄로 표시됩니다.

( 1.1 1.2 )
( 2.1 2.2 )

4. fmt::Formatter의 주요 메서드

가. width(): Option<usize>

  • 출력 시 최소 너비를 설정하는 값입니다.
  • Some(n)이면, 출력 문자열이 최소 n칸은 차지해야 함을 의미합니다.
  • 예: println!(“{:5}”, “hi”) → ” hi” (공백 3개 + hi)

사용 예:

fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let width = f.width().unwrap_or(0);
write!(f, "{:width$}", "hi", width = width)
}

나. precision(): Option<usize>

  • 소수점 이하 자리수 또는 최대 출력 길이를 설정합니다.
  • Some(n)이면, 최대 n자리 또는 n글자까지만 출력합니다.

사용 예:

fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let prec = f.precision().unwrap_or(2);
write!(f, "{:.prec$}", 3.14159, prec = prec)
}
// precision이 2이면 → 3.14

또는 문자열에 적용하면:

write!(f, "{:.5}", "Hello, world!") // → Hello

다.align(): Option<fmt::Alignment>

  • 정렬 방식 설정입니다. 리턴값은 Option이고, Alignment는 열거형입니다.
의미
Some(Left)왼쪽 정렬 (:<)
Some(Right)오른쪽 정렬 (:>)
Some(Center)가운데 정렬 (:^)
None정렬 지정 없음

사용 예:

use std::fmt::{self, Alignment};

fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match f.align() {
Some(Alignment::Left) => write!(f, "왼쪽 정렬됨"),
Some(Alignment::Right) => write!(f, "오른쪽 정렬됨"),
Some(Alignment::Center) => write!(f, "가운데 정렬됨"),
None => write!(f, "기본 정렬"),
}
}

라. Matrix 구조체에 width, precision 적용하기

impl fmt::Display for Matrix {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let w = f.width().unwrap_or(5);
let p = f.precision().unwrap_or(2);
write!(
f,
"( {:>w$.p$} {:>w$.p$} )\n( {:>w$.p$} {:>w$.p$} )",
self.0, self.1, self.2, self.3,
w = w, p = p
)
}
}

사용:

let m = Matrix(1.2345, 12.3456, 123.4567, 1234.5678);
println!("{:8.1}", m); // 최소 너비 8칸, 소수점 아래 1자

출력:

(     1.2    12.3 )
( 123.5 1234.6 )

바. format!와 write!

방식장점
format!() 사용문자열을 먼저 만들어 두기 편함
write!() 직접 사용메모리 절약, 포맷 버퍼 덜 생성함

Rust에서는 둘 다 가능하지만, 성능이나 메모리를 조금 더 아끼고 싶다면 write!()를 직접 쓰는 방식이 더 낫습니다.

5. 튜플 위치 변경(reverse)

// Tuples can be used as function arguments and as return values.
fn reverse(pair: (i32, bool)) -> (bool, i32) {
    // `let` can be used to bind the members of a tuple to variables.
    let (int_param, bool_param) = pair;

    (bool_param, int_param)
}

fn main() {
    let pair = (1, true);
    println!("Pair is {:?}", pair);

    println!("The reversed pair is {:?}", reverse(pair));
}
  • fn reverse(pair: (i32, bool)) -> (bool, i32) {
    => reverse 함수는 pair라는 인수를 받는데, i32와 bool 타입의 튜플이며, 반환 형식은 bool, i32 타입의 튜플입니다.
  • let (int_param, bool_param) = pair;
    => pair라는 튜플에서 값을 튜플 형식으로 받는데 int_param과 bool_param은 튜플의 멤버와 묶입니다.
  • (bool_param, int_param)은 표현식으로 reverse 함수의 return 값입니다. 인수는 i32, bool 타입이었는데, 순서가 바뀌어서 bool, i32 타입으로 반환됩니다.
  • let pair = (1, true);
    => pair란 변수에 1, true를 멤버로 하는 튜플을 대입합니다.
  • println!(“Pair is {:?}”, pair);
    => pair란 튜플을 debug 포맷으로 출력합니다.
  • println!(“The reversed pair is {:?}”, reverse(pair));
    => reverse 함수를 이용해 pair 튜플의 순서를 바꿔서 출력합니다.
  • cargo run으로 실행하면
    입력한 값대로 (1, true)로 출력되고,
    그 다음은 reverse 함수가 적용되어 순서가 바뀌어 (true, 1)로 출력됩니다.
Tuple 필드 reverse

6. 구조체 위치 변경(transpose)

use std::fmt;

#[derive(Debug)]
struct Matrix (f32, f32, f32, f32);

impl fmt::Display for Matrix {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "( {} {} )\n( {} {} )",
            self.0,self.1, self.2,self.3
        )
    }
}

fn transpose(matrix: Matrix) -> Matrix {
    Matrix(matrix.0, matrix.2, matrix.1, matrix.3)
}

fn main() {
    let matrix = Matrix(1.1, 1.2, 2.1, 2.2);
    println!("{:?}", matrix);
    println!("Matrix:\n{}", matrix);
    println!("Transpose:\n{}", transpose(matrix));
}
fn transpose(matrix: Matrix) -> Matrix {
Matrix(matrix.0, matrix.2, matrix.1, matrix.3)
}
  • fn transpose(matrix: Matrix) -> Matrix
    => 이 부분을 함수 signature라고 부르며,
    의미는 transpose 함수를 만드는데, 인수는 matrix이고, matrix의 타입은 Matrix 구조체입니다. 또한 반환 형식도 Matrix 구조체입니다.
  • { Matrix(matrix.0, matrix.2, matrix.1, matrix.3) }
    => 중괄호 안에 있는 것은 함수 본문으로 세미콜론이 없기 때문에 반환값을 나타내는 표현식입니다.

    Matrix(matrix.0, matrix.2, matrix.1, matrix.3)는 matrix라는 인수의 필드에 접근하는데, 인덱스가 0, 1, 2, 3이지만 순서를 바꾸기 위해 0, 2, 1, 3으로 표시했습니다. 이들 필드를 결합해서 Matrix 구조체를 반환합니다.
    println!("Matrix:\n{}", matrix);
println!("Transpose:\n{}", transpose(matrix));

fmt::Display trait을 이용해 matrix와 transpose(matrix)를 출력합니다.

출력 결과는
Matrix:
( 1.1 1.2 )
( 2.1 2.2 )
Transpose:
( 1.1 2.1 )
( 1.2 2.2 )
입니다.

1.2와 2.1의 위치가 달라졌습니다.