🦀 Rust

[Rust 입문] 5. 구조체 타입

date
Mar 23, 2023
slug
rust-struct
author
status
Public
tags
Rust
독학
summary
Rust의 구조체에 대한 설명입니다.
type
Post
thumbnail
category
🦀 Rust
updatedAt
Mar 23, 2023 11:18 AM
구조체는 서로 다른 타입을 지니고 있는 튜플과 유사하지만 구조체는 각 구성요소에 이름을 붙일 수 있다.

구조체의 정의

구조체는 Struct 키워드로 정의한다.
struct User { id: String, username: String, email: String, }

구조체의 사용

구조체를 사용하려면 구조체에 대한 인스턴스를 생성해야 한다. 인스턴스를 생성할 때 중괄호를 이용해 구조체의 구성요소에 값을 삽입하여야 한다.
구조체에 값을 삽입하는 형태는 자바스크립트에서 객체를 만드는 것과 유사한 형태이다.
값을 읽어 올 때에도 인스턴스에 점을 찍어 읽어올 수 있다.
fn main() { let user = User { id: String::from("puju"), username: String::from("puju"), email: String::from("daleaderprofq@gmail.com"), }; println!("{}, {}, {}", user.username, user.email, user.id); } struct User { id: String, username: String, email: String, }
만약 인스턴스를 mutable하게 정의한다면 인스턴스를 정의한 이후 필드의 값을 변경할 수 있다.
인스턴스는 내부 필드 중 특정 필드만 immutable/mutable하게 하는 것을 허용하지 않는다.
fn main() { let mut user = User { id: String::from("puju"), username: String::from("puju"), email: String::from("daleaderprofq@gmail.com"), }; println!("{}, {}, {}", user.username, user.email, user.id); user.id = String::from("joopark"); println!("{}, {}, {}", user.username, user.email, user.id); } struct User { id: String, username: String, email: String, }
구조체를 만들 때 인스턴스를 위와 같이 생성하는 것이 장황해 보일수도 있다. 새 인스턴스 구조체를 함수의 반환값(표현식)으로 하여 인스턴스를 만드는 동작을 함수로 처리할수도 있다.
fn main() { let mut user = gen_user( String::from("puju"), String::from("puju"), String::from("daleaderprofq@gmail.com") ); println!("{}, {}, {}", user.username, user.email, user.id); user.id = String::from("joopark"); println!("{}, {}, {}", user.username, user.email, user.id); } fn gen_user(id: String, username: String, email: String) -> User { User { id: id, username: username, email: email, } } struct User { id: String, username: String, email: String, }
위의 예제를 보면 함수의 인자 (변수명)와 구조체의 필드명이 동일한 것을 확인할 수 있다. rust에서 변수명이 구조체의 필드명과 동일할 때 한번만 쓰도록 하는 문법을 지원한다. 이를 필드 초기화 축약법 (field init shorthand)라고 한다.
자바스크립트에서 객체를 생성할 때에도 이와 유사한 문법을 지원한다.
fn gen_user(id: String, username: String, email: String) -> User { User {id, username, email} }

구조체 갱신 문법

기존에 존재하는 구조체 인스턴스로부터 일부 필드만 변경하여 새 인스턴스를 만들 때 필드를 일일히 모두 명시하면 코드가 길어지게 된다.
자바스크립트의 객체 내에서의 전개 문법과 유사하게 rust에서 구조체의 필드를 재사용할 때 구조체 갱신 문법 .. 을 사용할 수 있다.
fn main() { let user = User { id: String::from("puju"), username: String::from("puju"), email: String::from("daleaderprofq@gmail.com"), }; println!("{}, {}, {}", user.username, user.email, user.id); let new_user = User { id: String::from("joopark"), ..user }; println!("{}, {}, {}", new_user.username, new_user.email, new_user.id); } struct User { id: String, username: String, email: String, }

튜플 구조체

필드의 타입만 정의하고 필드의 이름은 지정하지 않는 튜플 구조체라는 것도 있다.
fn main() { let rgb1 = RGB(1, 2, 3); println!("{}, {}, {}", rgb1.0, rgb1.1, rgb1.2); } struct RGB (i32, i32, i32);

디버그

구조체의 멤버 변수들에 대한 값을 출력하기 위해 Debug라는 출력 포맷을 사용할 수 있다.
구조체의 정의 부분에 #[derive(Debug)] 어노테이션을 추가하고 println 매크로에서 디버그 포맷팅을 사용하면 구조체의 필드 값들을 출력하게 할 수 있다.
fn main() { let rgb1 = RGB(1, 2, 3); println!("{}, {}, {}", rgb1.0, rgb1.1, rgb1.2); println!("{:?}", rgb1); } #[derive(Debug)] struct RGB (i32, i32, i32);

메소드

구조체의 멤버 (혹은 열거형, 트레잇 객체) 내에 정의되는 함수를 메소드라고 한다. 메소드의 첫번째 파라미터는 항상 self이며 메소드가 호출되고 있는 구조체의 인스턴스를 나타낸다.
메소드는 impl (implementation) 블록 내에 구현한다.
fn main() { let rec1 = Rectangle::gen(10, 13); println!("rec1 size : {}", rec1.area()); } struct Rectangle { width: u32, height: u32 } impl Rectangle { fn gen(width: u32, height: u32) -> Rectangle { Rectangle {width, height} } fn area(&self) -> u32 { self.width * self.height } }
메소드에선 메소드가 소속되어 있는 구조체의 멤버변수를 self 키워드를 통해서 접근한다.
위 area 메소드를 보면 self 함수의 참조자를 집어넣는 것을 볼 수 있다. 참조자를 집어넣는데 별도로 타입을 명시하지 않는 것 (예를 들면 area(self: &Rectangle) 와 같이 쓰지 않는 것은 메소드가 Rectangle 내에 정의되어 있기 때문에 별도로 명시를 할 필요가 없기 때문에 생략하는 것이다.
소유권이 이전되도록 self 를 쓸수도 있으며 값을 읽어오기만 할 때엔 &self 를 쓰며 값을 변경할 필요가 있을 때에는 &mut self 를 쓸 수도 있다.
만약에 C++ 코드로 위와 비슷한 코드를 짠다면 화살표 연산자를 이용해야 할 것이다. 하지만 rust는 위에서 dot 연산자로 접근하게 해주며 rust는 자동 참조 및 역참조를 수행해주기 때문에 C++처럼 포인터나 객체이냐에 따라 화살표 연산자와 dot 연산자를 구별해서 사용할 필요가 없다.
C++나 자바의 클래스 내의 static 함수처럼 rust에도 연관 함수라는 것이 있다. (명칭이 메소드가 아니라 함수인 것에 유의하자.) 연관 함수는 impl 블럭 내에 정의되었지만 self 파라미터를 갖지 않는다. 이 경우 이 함수를 호출할 때엔 dot 연산자가 아니라 :: 연산자로 접근해 호출한다.