프로그래밍 언어/[Rust]

[Rust] formatting

swc0317 2024. 12. 26. 01:46
728x90
반응형
std::fmt 모듈 내 매크로

 

print!() 및 println!()

 

io:stdout, 즉 표준 출력에 입력된 string을 전달하는 매크로입니다.

 

둘의 차이는 마지막에 개행 문자 없이도 자동으로 개행 문자를 넣어주냐 마냐입니다.

 

 

이렇게 두 "Hello, World!" 구문을, 개행 문자를 포함해 출력해 보겠습니다.

 

순서대로

 

 

첫 줄은 개행 문자가 하나만, 두 번째 줄은 두 개가 출력되어, 다음 출력과 한 줄 띄어진 모습입니다.

 

eprint!() 및 eprintln!()

 

에러문 출력을 위한 출력 매크로입니다. 표준 에러에 입력된 문자열을 전달합니다.

 

format!()

 

문자열을 주어진 식별자에 전달하는 매크로입니다. print! 와의 차이는 

 

표준 출력에 전달하느냐, 아니면 주어진 식별자에 전달하느냐입니다.

 

 

let mut str: String을 통해, 문자열 값을 저장할 수 있는 식별자 "str"을 정의했습니다.

 

이에, format! 함수를 이용해, 문자열을 전달할 수 있고,

 

println! 을 통해, 이 식별자에 담긴 값을 formatting 기법을 이용해 출력합니다.

 

 

formatting

 

C style formatting과 비슷합니다.

 

편리한 점은, %d, %s와 같이 타입형을 명시해주지 않아도 됩니다.

 

    println!("{} days", 31);

 

가장 기본 형태입니다. 이런 식으로 문자열 사이에 {}를 넣고,

그다음 인자로 formatting 하고 싶은 값을 넣으면 이렇게 전달할 수 있습니다.

 

 

보시면 31 days가 출력된 모습을 볼 수 있습니다.

 

당연하게도, 인자들을 여러 개 쓸 수 있습니다. 이때

 

인자들이 들어온 순서대로 포매팅됩니다.

 

let name = "Alice";
let age = 30;
println!("Name: {}, Age: {}", name, age);

 

 

 

또는, 인자들을 중복하여 사용하고 싶을 때, 인자들에 인덱싱을 매기고

 

중복하여 사용할 수 있습니다.

 

 

formatting 인덱싱

 

    println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob");

 

두 개의 인자만 문자열에 추가로 포함되지만, 필요한 {} formatting은 4개인 모습.

 

 

이때 format은 입력받은 인자의 개수를 확인합니다. 사용된 인자보다,

 

주어진 인자가 적다면 에러를 일으킵니다.

 

 println!("My name is {0}, {1} {0}", "Bond");

2개의 인자를 요구하는 구문, 주어진 1개의 인자

 

 

 

 

 

또는, 각 formatting에 식별자를 할당하여, 명시적으로 출력할 수도 있습니다.

 

    println!("{subject} {verb} {object}",
             object="the lazy dog",
             subject="the quick brown fox",
             verb="jumps over");

 

 

formatting Base 설정

 

C style처럼, 정수형의 출력 형식을 명시할 수도 있습니다.

 

    println!("Base 10:               {}",   69420); // 69420
    println!("Base 2 (binary):       {:b}", 69420); // 10000111100101100
    println!("Base 8 (octal):        {:o}", 69420); // 207454
    println!("Base 16 (hexadecimal): {:x}", 69420); // 10f2c

 

보시는 바와 같이, 69420이라는 정수값을 각각

 

10진수, 2진수, 8진수, 16진수로 출력하기 위한 형태입니다.

 

 

formatting 여백 설정

 

      println!("{number:>5}", number=1);
      println!("{number:0>5}", number=1); // 00001
      println!("{number:0<5}", number=1); // 10000
      println!("{number:0>width$}", number=1, width=5);

 

이렇게 여백을 설정할 수 있습니다.

 

>, <는 각각 주어진 값이 어디에 위치할지,

 

그전에 주어진 값은 뭘로 여백을 채울지 결정합니다.

 

특히, 여백의 너비를 literal이 아닌, 변수형으로 정의할 땐 $을 통해 

 

이용할 수 있습니다.

 

 

혹은, 간단하게 방향을 설정하지 않아도 자동으로 맨 오른쪽에 출력되긴 합니다.

 

    println!("0011 AND 0101 is {:04b}", 0b0011u32 & 0b0101);
    println!("0011 OR 0101 is {:04b}", 0b0011u32 | 0b0101);
    println!("0011 XOR 0101 is {:04b}", 0b0011u32 ^ 0b0101);

 

 

소수 출력

 

 

      let mut number: f64 = 1.1591273;
      let width: usize = 5;
      println!("{number:>width$}");
      number = 1.0;
      println!("{number:>width$}");

 

1.0 같은 소수부는 깔끔하게 1로 출력하는 모습입니다.

 

 

 

debug formatting

 

음, 디버깅하니까 말이 좀 거창하긴 한데요.

 

튜플과 같이 단일 타입형이 아닌

 

여러 타입을 포함한 긴 자료 구조의 내부 값들을

 

반복문 없이도, 명시적으로 보여줄 수 있는 formatting도 존재합니다.

 

    let tuple_of_tuples = ((1u8, 2u16, 2u32), (4u64, -1i8), -2i16);

    // Tuples are printable.
    println!("tuple of tuples: {:?}", tuple_of_tuples);

 

배열과 다르게, 튜플은 각 원소들의 타입이

 

homogeneous하지 않습니다. 즉 위처럼 튜플 내 

 

정수뿐만 아니라, 또 다른 튜플이 존재할 수도 있고, 

 

정수끼리도 각 정수의 크기가 다른 타입형이 같은 튜플 내 저장될 수 있습니다.

 

이렇게 반복문으로 traverse하는 데 골치 아픈 구조일 수 있잖아요.

 

단순히 {:?} formatting 을 통해 해결할 수 있습니다.

 

 

특히 튜플뿐만 아니라, 모든 표준 라이브러리 내 자료구조들의

 

구조를 간단하게 프린트할 수 있는 포메팅이라 설명합니다.

 

"All std library types are automatically printable with {:?} too:"

 

https://doc.rust-lang.org/rust-by-example/hello/print/print_debug.html

 

Debug - Rust By Example

All types which want to use std::fmt formatting traits require an implementation to be printable. Automatic implementations are only provided for types such as in the std library. All others must be manually implemented somehow. The fmt::Debug trait makes

doc.rust-lang.org

 

특히, user-defined 구조체의 출력 formatting을 깔끔하게 정리할 수도 있습니다.

#[derive(Debug)]
struct Person<'a> {
    name: &'a str,
    age: u8
}

 

예를 들어 이렇게 #[derive(Debug)] 구문을 구조체 정의 위에

 

정의하면,

 

fn main() {
    let name = "Peter";
    let age = 27;
    let peter = Person { name, age };

    // Pretty print
    println!("{:#?}", peter);
}

 

이렇게 정의된 자료구조의 출력 formatting을 단 한줄만으로 깔끔하게

 

정리해줍니다.

 

 

 

 

마지막으로 모든 예시를 종합하여

 

이해를 도울 수 있는 코드 예시입니다.

 

    println!("{1:?} {0:?} is the {actor:?} name.",
             "Slater",
             "Christian",
             actor="actor's");

 

 

println에 출력되는 문자열에 3개의 인자가 formatting으로 사용되고,

 

순서대로 "Slater", "Christian", actor = "actor's"라는 인자가 들어가고,

 

인덱싱을 이용해, 첫 번째 포매팅엔 두 번째 인자가,

 

두 번째 포매팅엔 첫 번째 인자가,

 

세 번째 포매팅엔 식별자 actor가 담깁니다.

728x90
반응형

'프로그래밍 언어 > [Rust]' 카테고리의 다른 글

[Rust] enum  (0) 2024.12.30
[Rust] 구조체  (0) 2024.12.30
[Rust] Ownership과 Reference  (0) 2024.12.30
[Rust] 기본 골자  (2) 2024.12.26
[Rust] Visual Studio Code로 개발 환경 세팅  (3) 2024.12.25