강좌10-Struct 구조체

3 분 소요

Struct에 대해 알아봅시다.

Struct는 변수의 모음이며, 마치 새로운 형을 선언하는 것처럼 변수의 형을 Struct로 선언할 수 있다는 특징이 있습니다.

1. 생성

c의 struct 또는 typescript의 interface와 같은 구조체를 만들 수 있습니다.

struct User {
    name: String,
    email: String,
    age: u64,
    active: bool,
}

2. 변수 선언

구조체 변수는 다음과 같이 선언합니다.

let user = User {
  name: String::from("a"),
  email: String::from("a@b.com"),
  age: 10,
  active: true
}

함수에서는 다음과 같이 사용합니다.

fn set_user(param: User) -> User {
User {
 name: param.name,
 email: param.email,
 age: param.age
 active: param.active
 }
}

3. key 값의 생략

변수명과 key값이 동일한 경우 key를 생략할 수 있습니다.
또한, 생략과 생략하지 않는 경우를 함께 사용할 수도 있습니다.

fn main() {
    let name = String::from("kim");
    let email = String::from("kim@email.com");
    let age = 10;

    let user: User = User {
        name,
        email,
        age,
        active: true
    };
}

그러나 이름이 다르면 에러를 발생하므로 유의 하여야 합니다.


fn main() {
    let name1 = String::from("kim");
    let email = String::from("kim@email.com");
    let age = 10;
    let active = true;

    let user: User = User {
        name1,
        email,
        age,
        active
    };
}

//결과
  |
7 |         name1,
  |         ^^^^^ help: a field with a similar name exists: `name`

4. 동일한 구조체의 다른 변수로부터 일부 값 가져오기

같은 구조체를 가진 다른 변수로 부터 전체 / 일부 값을 가져올 수 있습니다.

fn main() {
    let user: User = User {
        name: String::from("kim"),
        email: String::from("kim@email.com"),
        10,
        true
    };

    let user2: User = User {
        name: String::from("lee"),
        email: String::from("lee@email.com"),
        ..user
    };

    let user3 = &user;
    
    let rtn_usr = struct_fn(user);
}

user2는 일부, user3는 전체를 가져왔습니다.

user3에서 ..user로 전체 값을 가져오는 것은 가능할까요? 이 때, user는 어떻게 될까요?

fn main() {
    let user: User = User {
        name: String::from("kim"),
        email: String::from("kim@email.com"),
        10,
        true
    };

    let user3 = ..user;
    
    let rtn_usr = struct_fn(user);
}

//결과
  |
2 |     let user: User = User {
  |         ---- move occurs because `user` has type `User`, which does not implement the `Copy` trait
...
9 |     let user3 = ..user;
  |                   ---- value moved here
...
11 |     let rtn_usr = struct_fn(user);
   |                             ^^^^ value used here after move

user의 값은 user3로 move되었기 때문에 user의 ownership이 사라져 이후로 사용할 수 없으므로 에러가 발생합니다.

5. Tuple 형태로 구조체 만들기

구조체를 만들 때 Tuple을 활용하면 key없이 간단하게 구성할 수 있습니다.

struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);

6. &str 형은 그대로 사용할 수 없다.

String형과는 달리 &str형은 구조체에서 그대로 사용할 수 없습니다.

이는 lifetime 때문인데 다른 챕터에서 더 자세히 알아보도록 하고,
여기에서는 &str은 구조체에 적합하지 않다. 라고만 이해하면 됩니다.

struct User {
    name: &str,
    email: String,
    age: u64,
    active: bool
}

fn main() {
    let user: User = User {
        key: "kim",
        email: String::from("kim@email.com"),
        age: 10,
        active: true
    };
}

// 에러 발생
  |
2 |     name: &str,
  |           ^ expected lifetime parameter

7. 함수에 구조체의 주소값만 넘기기

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

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

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

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

8. 구조체를 통째로 출력하기

구조체를 그대로 println의 인자로 넣으면 에러가 발생합니다

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

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

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

// 결과
  |
9 |     println!("rect1 is {}", rect1);
  |                             ^^^^^ `Rectangle` cannot be formatted with the default formatter
  |

해결법은 간단한데 이전 챕터의 println 출력 옵션을 보면 {:?} 이 있습니다.
이를 활용하면 구조체를 그대로 출력할 수 있습니다.

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

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

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

이번 강좌에서는 Struct의 기본 구조 및 편리한 사용 방법에 대해 알아보았습니다.

다음 강좌는 Struct의 확장에 대해 정리해보겠습니다.

댓글남기기