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

11967 단어 RustRust

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

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
}

좋은 웹페이지 즐겨찾기