[0x03] Using Structs to Structure Related Data
struct키워드를 이용해서 구조체를 만들 수 있다.
1. Defining and Instantiating Structs
1.1 Structure
tuple 과 다르게 각 필드에 대해 이름을 붙일 수 있다.
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}
- 구조체를 만든 다음에는
key : value 구조로 instance 를 만들어서 사용할 수 있다.
- 인스턴스를 만들 때, 필드의 순서를 바꿔서 적을 수도 있다.
fn main() {
let user1 = User {
email: String::from("[email protected]"),
username: String::from("someone"),
active: true,
sign_in_count: 1,
};
}
- 구조체에서
. 을 이용해 특정 값을 얻어올 수 있다.
- 인스턴스가
mutable 하게 선언되었다면, 값을 변경하는 것도 가능하다.
fn main() {
let mut user1 = User {
email: String::from("[email protected]"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
user1.email = String::from("[email protected]");
}
- 함수를 이용해서 변경하는 것도 가능하다.
fn build_user(email:String, username:String) -> User {
User {
email: email,
username: username,
active: true,
sign_in_count: 1,
}
}
1.1.1 Using the Field Init Shorthand
Rust 에서는 필드와 함수 인자의 이름이 같을 경우, 생략할 수 있다.
fn build_user(email: String, username: String) -> User {
User {
email,
username,
active: true,
sign_in_count: 1,
}
}
1.1.2 Creating a new Instance w/ other Instance
- 또, 다른
struct 로 부터의 값을 이용해서 새로운 인스턴스를 만들 수 있다.
fn main() {
// --skip--
let user2 = User {
active: user1.active,
username: user1.username,
email: String::from("[email protected]"),
sign_in_count: user1.sign_in_count,
};
}
Rust 에서는 이것도 줄일 수 있는 방법으로 .. 문법을 제공한다.
fn main() {
// --skip--
let user2 = User {
email: String::from("[email protected]"),
..user1
};
}
- 한 가지 주목할 점은
user2 인스턴스를 생성하며, = 연산자를 사용한 점이다.
- 이는
Ownership 에 대해 설명할 때 살펴본 move를 했기 때문이다.
- 따라서,
user1 은 user2를 생성한 후 소유권을 잃고 사용할 수 없게 된다.
- 구체적으로 말하면,
힙 영역을 사용하는 데이터들(email, username)이 move 되어버렸기 때문이다.
- 따라서,
user2 의 username도 따로 선언해준다면 copy 만 일어나게 되고, 결과적으로user1도 문제 없이 사용할 수 있다.
1.2 Tuple Structure
tuple structure 에서는 각 필드에 대한 이름이 없다.
tuple 에 이름을 붙일 때 유용하게 사용할 수 있다.
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
fn main() {
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
}
black 과 origin 은 각각 서로 다른 tuple structure의 인스턴스이므로 서로 다른 타입이다.
- 즉, 필드가 같은 형태로 구성되어 있다고 하더라도 이름이 다르면 서로 다른 타입이다.
tuple structure는 tuple과 마찬가지로 아래 두 가지가 가능하다.
destructuring
. 연산자로 값 참조
1.3 Unit-Like structs
- 필드가 아예 없는
structure를 만들 수도 있다.
- 이들은
unit-like structs라 불리고, ()와 비슷하게 작동한다.
unit-like structs 는 trait을 구현할 때 특히 유용하다.
- 특정
type 이 타입 자체에 저장하려는 데이터가 없을 때, ()를 사용할 수 있다.
struct AlwaysEqual;
fn main() {
let subject = AlwaysEqual;
}
Ownership Of Struct Data
- 위에서 살펴본
User 구조체에서 &str 대신에 String 타입을 사용한 것은 Rust 의 lifetime 개념에 대한 이해가 필요하다.
2. Structs Example
- 직사각형의 넓이를 구하는 코드를 작성하며
struct에 대한 이해를 해보자.
[1]
struct 없이, 단순한 코드를 작성한다.
fn main() {
let width1 = 30;
let height1 = 50;
println!(
"The area of the rectangle is {} square pixels.",
area(width1, height1)
);
}
fn area(width: u32, height: u32) -> u32 {
width * height
}
[2] +Tuple
tuple 을 사용하여 코드를 작성한다.
fn main() {
let rect1 = (30, 50);
println!(
"The area of the rectangle is {} square pixels.",
area(rect1)
);
}
fn area(dimensions: (u32, u32)) -> u32 {
dimensions.0 * dimensions.1
}
[3] +Structs
structs 를 사용하여 보다 의미 있는 코드를 작성한다.
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 { // one parameter!
rectangle.width * rectangle.height
}
[4] +Debugging
- 코딩 중에 디버깅을 하는 것이 필요한 순간이 있다.
- 이 때,
println! 을 통해 데이터 값을 체크 할 수 있는데, 이 때 Display라는 format 을 사용한다.
td::fmt::Display 이다.
struct 의 경우에는 출력하는 포맷의 모호함 때문에 따로 Display 의 구현을 제공하지 않는다.
struct 의 내용을 출력하고 싶으면 {} 대신에 {:?} 또는 {:#?} (for pretty-print) 를 쓰면 된다.
- 또,
struct 에 Debug 라는 trait 이 구현되어 있지 않으면 #[derive(Debug)] 를 적어주어야 한다.
#[derive(Debug)] // implements Debug trait
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let a = Rectangle{
width: 30,
height: 20,
};
println!("The info of area: {:?}", a); // formatting for structs
println!("The size of area is {}", area(a));
}
fn area(rectangle: Rectangle) -> u32 {
rectangle.width * rectangle.height
}
- 또 다른 방법은
dbg! 매크로를 사용하는 것이다.
dbg! 를 사용하면 코드 라인까지 출력해준다.
[5] misc
Rust 에서는 Debug 외에도 여러가지 trait 을 제공한다.
- 이러한
trait 들은 structs와 같은 custom type 을 만들 때 많이 유용하다.
3. Method Syntax
method 는 함수와 비슷하지만, structs 의 컨텍스트 내부에서 정의된다.
- 또한, 첫 번째 인자로
self 를 받는다.
self 는 해당 메소드를 호출하는 structs 의 instance를 가리킨다.
3.1 Defining Method
method 의 정의는 아래 코드를 참고하자.
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
);
}
- 여기서
&self 는 사실 self:&Self 의 축약이다.
Self 는 impl 블록의 타입을 의미한다.
- 일반적으로
method 들은 self 의 소유권을 가져오는 경우가 드물기 때문에, 대부분 &self를 사용한다.
method 를 사용하는 가장 큰 이유는 특정 타입에 대한 함수들을 한 곳에 모아두기 위함이다.
- 다른 언어들에서는 getter 라 불리는
method를 많이 구현해준다.
- 이는, 필드 값을 read-only 로 바로 참조할 수 있도록 해주는
method 이다.
- 하지만,
Rust 에서는 이를 자동으로 구현해주지 않는다.
3.2 Methods w/ more parameters
- 예제 코드는 아래와 같다.
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
3.3 Associated Functions
- 모든
method 를 Associated Function 이라고 부를 수 있다.
self 를 첫 번째 인자로 갖지 않는, association function을 구현하는 것이 가능하다.
- 사실
String 에서도 String::from() 에는 self가 없다.
- 보통 이런 함수는
constructor 에 쓸 수 있다.
impl Rectangle {
fn square(size: u32) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
}
- 이러한
association function은 :: 연산자로 호출할 수 있다.
- 예를 들어,
let sq = Rectangle::square(3) 이런 식이다.
3.4 Multiple impl Blocks
impl 블록이 하나만 있어야 한다는 법은 없다.
- 아래와 같은 코드도 유효하다.
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
Author And Source
이 문제에 관하여([0x03] Using Structs to Structure Related Data), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://velog.io/@c0np4nn4/0x03-Using-Structs-to-Structure-Related-Data
저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
tuple 과 다르게 각 필드에 대해 이름을 붙일 수 있다.struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}key : value 구조로 instance 를 만들어서 사용할 수 있다.fn main() {
let user1 = User {
email: String::from("[email protected]"),
username: String::from("someone"),
active: true,
sign_in_count: 1,
};
}. 을 이용해 특정 값을 얻어올 수 있다.mutable 하게 선언되었다면, 값을 변경하는 것도 가능하다.fn main() {
let mut user1 = User {
email: String::from("[email protected]"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
user1.email = String::from("[email protected]");
}fn build_user(email:String, username:String) -> User {
User {
email: email,
username: username,
active: true,
sign_in_count: 1,
}
}Rust 에서는 필드와 함수 인자의 이름이 같을 경우, 생략할 수 있다.fn build_user(email: String, username: String) -> User {
User {
email,
username,
active: true,
sign_in_count: 1,
}
}struct 로 부터의 값을 이용해서 새로운 인스턴스를 만들 수 있다.fn main() {
// --skip--
let user2 = User {
active: user1.active,
username: user1.username,
email: String::from("[email protected]"),
sign_in_count: user1.sign_in_count,
};
}Rust 에서는 이것도 줄일 수 있는 방법으로 .. 문법을 제공한다.fn main() {
// --skip--
let user2 = User {
email: String::from("[email protected]"),
..user1
};
}user2 인스턴스를 생성하며, = 연산자를 사용한 점이다.Ownership 에 대해 설명할 때 살펴본 move를 했기 때문이다.user1 은 user2를 생성한 후 소유권을 잃고 사용할 수 없게 된다.힙 영역을 사용하는 데이터들(email, username)이 move 되어버렸기 때문이다.user2 의 username도 따로 선언해준다면 copy 만 일어나게 되고, 결과적으로user1도 문제 없이 사용할 수 있다.tuple structure 에서는 각 필드에 대한 이름이 없다.tuple 에 이름을 붙일 때 유용하게 사용할 수 있다.struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
fn main() {
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
}black 과 origin 은 각각 서로 다른 tuple structure의 인스턴스이므로 서로 다른 타입이다.tuple structure는 tuple과 마찬가지로 아래 두 가지가 가능하다.destructuring.연산자로 값 참조
structure를 만들 수도 있다.unit-like structs라 불리고, ()와 비슷하게 작동한다.unit-like structs 는 trait을 구현할 때 특히 유용하다.- 특정
type이 타입 자체에 저장하려는 데이터가 없을 때,()를 사용할 수 있다.
struct AlwaysEqual;
fn main() {
let subject = AlwaysEqual;
}Ownership Of Struct Data
- 위에서 살펴본
User구조체에서&str대신에String타입을 사용한 것은Rust의lifetime개념에 대한 이해가 필요하다.
- 직사각형의 넓이를 구하는 코드를 작성하며
struct에 대한 이해를 해보자.
[1]
struct없이, 단순한 코드를 작성한다.
fn main() {
let width1 = 30;
let height1 = 50;
println!(
"The area of the rectangle is {} square pixels.",
area(width1, height1)
);
}
fn area(width: u32, height: u32) -> u32 {
width * height
}
[2] +Tuple
tuple을 사용하여 코드를 작성한다.
fn main() {
let rect1 = (30, 50);
println!(
"The area of the rectangle is {} square pixels.",
area(rect1)
);
}
fn area(dimensions: (u32, u32)) -> u32 {
dimensions.0 * dimensions.1
}
[3] +Structs
structs를 사용하여 보다 의미 있는 코드를 작성한다.
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 { // one parameter!
rectangle.width * rectangle.height
}
[4] +Debugging
- 코딩 중에 디버깅을 하는 것이 필요한 순간이 있다.
- 이 때,
println!을 통해 데이터 값을 체크 할 수 있는데, 이 때Display라는 format 을 사용한다. td::fmt::Display이다.
struct의 경우에는 출력하는 포맷의모호함때문에 따로Display의 구현을 제공하지 않는다.struct의 내용을 출력하고 싶으면{}대신에{:?}또는{:#?}(for pretty-print) 를 쓰면 된다.- 또,
struct에Debug라는trait이 구현되어 있지 않으면#[derive(Debug)]를 적어주어야 한다.
#[derive(Debug)] // implements Debug trait
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let a = Rectangle{
width: 30,
height: 20,
};
println!("The info of area: {:?}", a); // formatting for structs
println!("The size of area is {}", area(a));
}
fn area(rectangle: Rectangle) -> u32 {
rectangle.width * rectangle.height
}
- 또 다른 방법은
dbg!매크로를 사용하는 것이다. dbg!를 사용하면 코드 라인까지 출력해준다.
[5] misc
Rust에서는Debug외에도 여러가지trait을 제공한다.- 이러한
trait들은structs와 같은 custom type 을 만들 때 많이 유용하다.
3. Method Syntax
method 는 함수와 비슷하지만, structs 의 컨텍스트 내부에서 정의된다.
- 또한, 첫 번째 인자로
self 를 받는다.
self 는 해당 메소드를 호출하는 structs 의 instance를 가리킨다.
3.1 Defining Method
method 의 정의는 아래 코드를 참고하자.
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
);
}
- 여기서
&self 는 사실 self:&Self 의 축약이다.
Self 는 impl 블록의 타입을 의미한다.
- 일반적으로
method 들은 self 의 소유권을 가져오는 경우가 드물기 때문에, 대부분 &self를 사용한다.
method 를 사용하는 가장 큰 이유는 특정 타입에 대한 함수들을 한 곳에 모아두기 위함이다.
- 다른 언어들에서는 getter 라 불리는
method를 많이 구현해준다.
- 이는, 필드 값을 read-only 로 바로 참조할 수 있도록 해주는
method 이다.
- 하지만,
Rust 에서는 이를 자동으로 구현해주지 않는다.
3.2 Methods w/ more parameters
- 예제 코드는 아래와 같다.
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
3.3 Associated Functions
- 모든
method 를 Associated Function 이라고 부를 수 있다.
self 를 첫 번째 인자로 갖지 않는, association function을 구현하는 것이 가능하다.
- 사실
String 에서도 String::from() 에는 self가 없다.
- 보통 이런 함수는
constructor 에 쓸 수 있다.
impl Rectangle {
fn square(size: u32) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
}
- 이러한
association function은 :: 연산자로 호출할 수 있다.
- 예를 들어,
let sq = Rectangle::square(3) 이런 식이다.
3.4 Multiple impl Blocks
impl 블록이 하나만 있어야 한다는 법은 없다.
- 아래와 같은 코드도 유효하다.
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
Author And Source
이 문제에 관하여([0x03] Using Structs to Structure Related Data), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://velog.io/@c0np4nn4/0x03-Using-Structs-to-Structure-Related-Data
저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
method 는 함수와 비슷하지만, structs 의 컨텍스트 내부에서 정의된다.self 를 받는다.self 는 해당 메소드를 호출하는 structs 의 instance를 가리킨다.method 의 정의는 아래 코드를 참고하자.#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
);
}&self 는 사실 self:&Self 의 축약이다.Self 는 impl 블록의 타입을 의미한다.method 들은 self 의 소유권을 가져오는 경우가 드물기 때문에, 대부분 &self를 사용한다.method 를 사용하는 가장 큰 이유는 특정 타입에 대한 함수들을 한 곳에 모아두기 위함이다.method를 많이 구현해준다.method 이다.Rust 에서는 이를 자동으로 구현해주지 않는다.impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}method 를 Associated Function 이라고 부를 수 있다.self 를 첫 번째 인자로 갖지 않는, association function을 구현하는 것이 가능하다.String 에서도 String::from() 에는 self가 없다.constructor 에 쓸 수 있다.impl Rectangle {
fn square(size: u32) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
}association function은 :: 연산자로 호출할 수 있다.let sq = Rectangle::square(3) 이런 식이다.impl 블록이 하나만 있어야 한다는 법은 없다.impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}Author And Source
이 문제에 관하여([0x03] Using Structs to Structure Related Data), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@c0np4nn4/0x03-Using-Structs-to-Structure-Related-Data저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)