간단하게 정리해본 Rust 강좌입니다.
다음 문서를 기반으로 작성했습니다.
Tour of Rust
https://tourofrust.com/00_ko.html
2022. 09. 08 최초작성
2023. 03. 27 최종작성
Rust 개발 환경 만드는 방법은 아래 포스트를 참고하세요.
https://webnautes.tistory.com/2110
메모리
Rust 프로그램에는 데이터가 저장되는 다음 세 가지의 메모리 영역이 있습니다.
- 데이터 메모리(data memory)
크기가 고정되어 있으며 프로그램이 실행되는 동안 사용 가능한 영역입니다. 예) 큰따옴표(“)로 둘러쌓인 문자열
- 스택 메모리(stack memory)
함수 내에서 변수가 선언될때 사용되는 영역입니다. 이 메모리에 저장된 것들은 함수가 호출된 동안에만 유지됩니다. 즉, 함수가 종료되는 순간 메모리에서 사라집니다.
- 힙 메모리(heap memory) - 프로그램이 실행되는 동안 사용되는 영역입니다. 이 영역의 데이터는 추가/이동/제거/크기 변경 등이 가능합니다.
열거형(enumeration)
enum 키워드를 사용하여 열거형 데이터 타입을 생성할 수 있습니다. 열거형 데이터 타입에 속한 원소를 match 또는 if 등의 조건으로 사용할 수 있습니다.
#![allow(dead_code)] // 이 줄은 컴파일러 경고를 방지해줍니다. // enum 데이터 타입을 생성합니다. enum Species { Crab, Octopus, Fish, Clam, } fn main() { // enum 데이터 타입의 원소 중 하나를 변수 ferris에 저장합니다. let ferris = Species::Crab; // 변수 ferris에 저장된 것을 match 문으로 비교하여 해당 문자열을 화면에 출력하도록 합니다. match ferris { Species::Crab => println!("게"), Species::Octopus => println!("문어"), Species::Fish => println!("물고기"), Species::Clam => println!("조개"), } } |
실행해보면 게가 출력됩니다.
if문을 사용하여 열거형 데이터 타입을 비교하는 예제입니다.
#![allow(dead_code)] // 이 줄은 컴파일러 경고를 방지해줍니다. #[derive(Eq, PartialEq)] // ==을 열거형 데이터 타입에서 사용할수 있게 해줍니다. // enum 데이터 타입을 생성합니다. enum Species { Crab, Octopus, Fish, Clam, } fn main() { // enum 데이터 타입의 원소 중 하나를 변수 ferris에 저장합니다. let ferris = Species::Crab; // 변수 ferris에 저장된 값이 enum 데이터 타입의 원소 Crab과 일치하면 문자열을 화면에 출력하도록 합니다. if ferris == Species::Crab{ println!("게"); } } |
실행해보면 게가 출력됩니다.
제네릭 데이터 타입(Generic Data Type)
Rust가 데이터 타입을 유추할 수 있지만 제네릭 데이터 타입을 사용하여 컴파일러가 컴파일 타임에 struct나 enum의 데이터 타입을 지정할 수 있도록 합니다. 이때 ::<T> 연산자를 사용하여 데이터 타입을 지정합니다.
#[derive(Debug)] //println!에서 구조체를 출력하기 위해 필요합니다. // 구조체의 제네릭 데이터 타입 버전입니다. struct BagOfHolding<T> { item: T, // T에 따라 item 필드의 데이터 타입이 결정됩니다. } fn main() { // 컴파일될때 사용할 구조체 필드의 데이터 타입을 지정합니다. let i32_bag = BagOfHolding::<i32> { item: 42 }; // i32 데이터 타입을 사용합니다. let bool_bag = BagOfHolding::<bool> { item: true }; //bool 데이터 타입을 사용합니다. // 구조체 필드의 데이터 타입을 지정하지 않아도 Rust는 데이터 타입을 유추할 수 있습니다. let float_bag = BagOfHolding { item: 3.14 }; // 구조체 안에 구조체를 정의합니다. let bag_in_bag = BagOfHolding { item: BagOfHolding { item: "쾅!" }, }; println!("{:?}", i32_bag); println!("{:?}", bool_bag); println!("{:?}", float_bag); println!("{:?}", bag_in_bag); } |
실행결과
값이 없음을 표현하기
Rust에는 값이 없음을 나타내기 위해 사용되는 null이 없습니다. 대신 필요시 None을 사용합니다.
Option
Rust에는 null을 쓰지 않고도 값이 없음을 표현할 수 있는 Option이라고 불리는 내장된 generic enum이 있습니다.
Option을 사용하면 값이 있는 경우 Some으로 표현되며 값이 없는 경우에는 None으로 표현됩니다.
pub enum Option<T> { None, Some(T), } |
Option을 사용하는 예제 코드입니다.
#[derive(Debug)] struct BagOfHolding<T> { item: Option<T>, } fn main() { // 값이 없는 None이라 하더라도 데이터 타입을 지정해줘야 합니다. let i32_bag_1 = BagOfHolding::<i32> { item: None }; // if 문을 사용하여 값이 있는지 체크합니다. if i32_bag_1.item.is_none() { println!("가방에서 {:?}를 찾았다!", i32_bag_1.item) } // 값이 있는 경우에는 Some을 사용합니다. let i32_bag_2 = BagOfHolding::<i32> { item: Some(42) }; // if 문을 사용하여 값이 있는지 체크합니다. if i32_bag_2.item.is_some() { println!("가방에서 {:?}를 찾았다!", i32_bag_2.item) } // match를 사용하여 Option 타입의 변수에 값이 있는지 체크합니다. match i32_bag_1.item { Some(v) => println!("가방에서 {}를 찾았다!", v), None => println!("아무 것도 찾지 못했다"), } // match를 사용하여 Option 타입의 변수에 값이 있는지 체크합니다. match i32_bag_2.item { Some(v) => println!("가방에서 {}를 찾았다!", v), None => println!("아무 것도 찾지 못했다"), } } |
실행결과
가방에서 None를 찾았다!
가방에서 Some(42)를 찾았다!
아무 것도 찾지 못했다
가방에서 42를 찾았다!
Result
Result는 Rust에서 오류 처리를 하는 데 사용되는 관용적인 방법입니다.
enum Result<T, E> { Ok(T), Err(E), } |
Result 타입을 사용하면 함수에서 리턴값으로 Ok 또는 Err을 사용할 수 있습니다.
// 호출한 곳으로 전달할 값을 Ok 또는 Err을 사용했냐에 따라 Result에 지정된 두가지 타입중 하나로 리턴됩니다. fn do_something_that_might_fail(i: i32) -> Result<i32, (String, i32)> { if i == 42 { Ok(i) // 값 하나만 리턴합니다. } else { Err((String::from("맞는 숫자가 아닙니다"), i)) // 튜플을 사용하여 두 개의 값을 리턴합니다. } } fn main() { let result = do_something_that_might_fail(12); match result { Ok(i) => println!("발견: {}는 맞는 숫자입니다.", i), Err((e, i)) => println!("오류: {}는 {}", i, e), } let result = do_something_that_might_fail(42); match result { Ok(i) => println!("발견: {}는 맞는 숫자입니다.", i), Err((e, i)) => println!("오류: {}는 {}", i, e), } } |
실행결과
우아한 오류 처리
Result와 함께 연산자 ?를 사용할 수 있습니다. 코드가 많이 간단해집니다.
fn do_something_that_might_fail(i: i32) -> Result<i32, (String, i32)> { if i == 42 { Ok(i) // 값 하나만 리턴합니다. } else { Err((String::from("맞는 숫자가 아닙니다"), i)) // 튜플을 사용하여 두 개의 값을 리턴합니다. } } fn test(i: i32) -> Result<i32, (String, i32)>{ let v = do_something_that_might_fail(i)?; Ok(v) } fn main() { let ret = test(42); println!("{:?}", ret); let ret = test(11); println!("{:?}", ret); } |
실행결과
Option, Result과 함께 unwrap를 사용
unwrap를 사용하면 Option/Result 내부의 값을 꺼내옵니다. 이때 enum이 None 또는 Err인 경우에는 panic이 발생합니다.
my_option.unwrap() 는 아래 코드와 동일하게 동작합니다.
match my_option {
Some(v) => v,
None => panic!("some error message generated by Rust!"),
}
my_result.unwrap()는 아래 코드와 동일하게 동작합니다.
match my_result {
Ok(v) => v,
Err(e) => panic!("some error message generated by Rust!"),
}
panic는 매크로로 호출된 경우 오류 메시지 및 오류난 파일 이름/줄 번호를 함께 출력하며 프로그램을 중지합니다.
Result에 대해 unwrap를 사용한 예제 코드입니다.
fn do_something_that_might_fail(i: i32) -> Result<i32, (String, i32)> { if i == 42 { Ok(i) // 값 하나만 리턴합니다. } else { Err((String::from("맞는 숫자가 아닙니다"), i)) // 튜를을 사용하여 두 개의 값을 리턴합니다. } } fn test(i: i32) -> i32{ let result = do_something_that_might_fail(i); result.unwrap() // result.unwrap()는 match를 사용하여 구현한 아래 코드와 동일하게 동작합니다. // match result { // Ok(i) => i, // Err((e, i)) => panic!("some error message generated by Rust!"), // } } fn main() { let ret = test(42); // 42가 줄력됩니다. println!("{}", ret); let ret = test(11); // 패닉이 발생합니다. println!("{}", ret); } |
실행결과
Option에 대해 unwrap를 사용한 예제입니다.
fn do_something_that_might_fail(i: i32) -> Option<i32> { if i == 42 { Some(i) } else { None } } fn test(i: i32) -> i32{ let result = do_something_that_might_fail(i); result.unwrap() // result.unwrap()는 match를 사용하여 구현한 아래 코드와 동일하게 동작합니다. // match result { // Some(i) => i, // None => panic!("some error message generated by Rust!"), // } } fn main() { let ret = test(42); // 42가 줄력됩니다. println!("{}", ret); let ret = test(11); // 패닉이 발생합니다. println!("{}", ret); } |
실행결과
벡터(vector)
vector는 Vec struct로 표현되는 가변 크기의 리스트입니다.
vec! 매크로를 사용하면 vector를 수동으로 일일이 만드는 대신, 손쉽게 생성할 수 있게 해줍니다.
Vec는 vector를 for 반복문에 손쉽게 넣을 수 있도록 vector로부터 반복자를 생성할 수 있는 iter() 메소드를 갖고 있습니다.
Vec은 구조체이지만 내부적으로는 heap에 있는 고정 리스트에 대한 참조를 포함하고 있습니다.
vector는 기본 크기를 갖고 시작하는데, 주어진 크기보다 많은 원소가 추가될 경우, 큰 크기를 가지는 새로운 고정 리스트를 위해 heap에 데이터를 재할당합니다.
fn main() { // 데이터 타입을 명시하여 벡터를 생성합니다. let mut i32_vec = Vec::<i32>::new(); // 벡터에 값을 삽입합니다. i32_vec.push(1); i32_vec.push(2); i32_vec.push(3); // 벡터의 값을 출력합니다. for num in i32_vec.iter() { print!("{} ", num); } println!(""); // 데이터 타입을 명시하지 않아도 됩니다. let mut float_vec = Vec::new(); float_vec.push(1.3); float_vec.push(2.3); float_vec.push(3.4); // 벡터의 값을 출력합니다. for num in float_vec.iter() { print!("{} ", num); } println!(""); // vec! 매크로를 사용하면 벡터에 값을 하나씩씩 삽입하지 않고 다음처럼 할 수 있습니다. let string_vec = vec![String::from("Hello"), String::from("World")]; // 벡터의 값을 출력합니다. for word in string_vec.iter() { println!("{}", word); } } |
실행결과
간단하게 정리해본 Rust 강좌 1 : 변수, 상수, 함수, 튜플, 배열, 데이터타입, println
https://webnautes.tistory.com/2194
간단하게 정리해본 Rust 강좌 2 : if-else, loop, while, for, match, struct, method
https://webnautes.tistory.com/2195
간단하게 정리해본 Rust 강좌 3 : 열거형, 제네릭 데이터 타입, Option, Result, unwrap, vector
https://webnautes.tistory.com/2196
간단하게 정리해본 Rust 강좌 4 : 소유권, 참조, 역참조, 생명주기
https://webnautes.tistory.com/2197
간단하게 정리해본 Rust 강좌 5 : 문자열, utf-8
https://webnautes.tistory.com/2198
간단하게 정리해본 Rust 강좌 6 : 모듈
https://webnautes.tistory.com/2199
간단하게 정리해본 Rust 강좌 7 : &self, &mut self, trait, 동적 디스패치, 정적 디스패치, Generic 메서드, Box
https://webnautes.tistory.com/2200
간단하게 정리해본 Rust 강좌 8 : 참조자, 댕글링 참조, 원시 포인터
https://webnautes.tistory.com/2203
간단하게 정리해본 Rust 강좌 9 : Box, 재귀적 데이터타입, Deref 트레잇
https://webnautes.tistory.com/2202
'Rust > Rust 강좌' 카테고리의 다른 글
간단하게 정리해본 Rust 강좌 6 : 모듈 (0) | 2023.10.28 |
---|---|
간단하게 정리해본 Rust 강좌 5 : 문자열, utf-8 (0) | 2023.10.28 |
간단하게 정리해본 Rust 강좌 4 : 소유권, 참조, 역참조, 생명주기 (0) | 2023.10.28 |
간단하게 정리해본 Rust 강좌 2 : if-else, loop, while, for, match, struct, method (2) | 2023.10.28 |
Rust 지정한 디렉토리의 파일/디렉토리 리스트 가져오기 (0) | 2023.10.13 |
시간날때마다 틈틈이 이것저것 해보며 블로그에 글을 남깁니다.
블로그의 문서는 종종 최신 버전으로 업데이트됩니다.
여유 시간이 날때 진행하는 거라 언제 진행될지는 알 수 없습니다.
영화,책, 생각등을 올리는 블로그도 운영하고 있습니다.
https://freewriting2024.tistory.com
제가 쓴 책도 한번 검토해보세요 ^^
그렇게 천천히 걸으면서도 그렇게 빨리 앞으로 나갈 수 있다는 건.
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!