[Chapter 5-2] Rust 구조체를 이용한 예제 프로그램

hwwwa·2021년 10월 28일
0

🦀 Rust

목록 보기
12/25

사각형의 넓이를 계산하는 프로그램을 작성해봅시다.

Cargo로 픽셀 단위로 명시된 사각형의 길이와 넓이를 입력받아 사각형의 넓이를 계산하는 rectangles 라는 이름의 새로운 바이너리 프로젝트를 만듭니다.

cargo new rectangles --bin

filename: src/main.rs

fn main() {
    let length1 = 50;
    let width1 = 30;

    println!(
        "The area of the rectangle is {} square pixels.",
        area(length1, width1)
    );
}

fn area(length: u32, width: u32) -> u32 {
    length * width
}

cargo run으로 실행하면 다음과 같은 결과를 얻을 수 있습니다.

The area of the rectangle is 1500 square pixels.

튜플을 이용한 리팩터링

위의 코드를 더 좋게 변경할 수 있습니다. area 함수의 두 파라미터들은 연관되어 있지만 코드 내 어디에도 표현되지 않았습니다. 튜플을 이용하여 이를 명시할 수 있습니다.

fn main() {
    let rect1 = (50, 30);

    println!(
        "The area of the rectangle is {} square pixels.",
        area(rect1)
    );
}

fn area(dimensions: (u32, u32)) -> u32 {
    dimensions.0 * dimensions.1
}

하지만 다른 한편으로는 튜플에 요소에 대한 이름이 없기 때문에 덜 명확하다고 할 수 있습니다. 튜플 내의 값을 인덱스로 접근해야 하기 때문에 계산이 혼란스러울 수 있습니다. 만약 면적을 구하는 것이 아닌 사각형을 화면에 그리는 상황이라면 문제가 발생할 것입니다.

구조체를 이용한 리펙터링: 의미 추가하기

데이터에 이름표를 붙여 의미를 부여하기 위해 구조체를 사용할 수 있습니다.

struct Rectangle {
    length: u32,
    width: u32,
}

fn main() {
    let rect1 = Rectangle { length: 50, width: 30 };

    println!(
        "The area of the rectangle is {} square pixels.",
        area(&rect1)
    );
}

fn area(rectangle: &Rectangle) -> u32 {
    rectangle.length * rectangle.width
}

area 함수는 하나의 파라미터를 갖도록 수정되었습니다. 이는 Rectangle 구조체 인스턴스의 불변 참조자 타입입니다. 구조체의 소유권을 얻기 보다는 빌리기를 원하므로, 함수 호출시에 &를 사용하여 main 이 소유권을 유지하고 rect1 을 계속 사용할 수 있게 됩니다.

파생 트레잇(derived trait)으로 유용한 기능 추가

struct Rectangle {
    length: u32,
    width: u32,
}

fn main() {
    let rect1 = Rectangle { length: 50, width: 30 };

    println!("rect1 is {}", rect1);

위의 코드는 Rectangle 인스턴스 출력을 시도하는 코드입니다. 코드를 실행시키면 다음과 같은 메세지와 함께 에러가 발생하게 됩니다.

error[E0277]: the trait bound `Rectangle: std::fmt::Display` is not satisfied

구조체를 사용하는 경우 println! 이 출력을 형식화하는 방법이 모호하기 때문입니다. 사용자가 쉼표를 이용하고 싶은지, 중괄호를 이용하고 싶은지 등이 모호합니다. Rust는 사용자가 원하는 것을 추론하려고 하지 않습니다.

에러 메세지 중에서 아래와 같은 도움말을 볼 수 있습니다.

note: `Rectangle` cannot be formatted with the default formatter; try using
`:?` instead if you are using a format string

이를 적용하여 println! 매크로 호출을 println!("rect1 is {:?}", rect1); 의 형태로 사용해봅시다. { } 내에 : ? 명시자를 넣는 것은 println! 에게 Debug라 불리는 출력 포맷을 사용하고 싶다고 알려주는 것입니다.

하지만 변경한 코드를 실행해도 여전히 에러가 발생합니다.

error: the trait bound `Rectangle: std::fmt::Debug` is not satisfied

note: `Rectangle` cannot be formatted using `:?`; if it is defined in your
crate, add `#[derive(Debug)]` or manually implement it

구조체에 대해 해당 기능을 활성화하도록 명시적인 사전동의가 필요합니다. 구조체 정의부분 바로 전에 #[derive(Debug)] 어노테이션을 추가합니다.

#[derive(Debug)]
struct Rectangle {
    length: u32,
    width: u32,
}

fn main() {
    let rect1 = Rectangle { length: 50, width: 30 };

    println!("rect1 is {:?}", rect1);
}
rect1 is Rectangle { length: 50, width: 30 }

println! 스트링 내에 { : ? } 대신 { : # ? } 을 사용할 수 있습니다. 출력은 아래와 같습니다.

rect1 is Rectangle {
    length: 50,
    width: 30
}

0개의 댓글