본문 바로가기

Rust/Basic

[Rust Tutorial] 4 - Data Type

 


 

Intro

Rust에서 제공하는 Data Type 여러 가지가 있지만 기본적으로 제공하는 Type은 크게 Primary Type과 Advenced Type, Compound Type, Collection Type으로 나눌 수 있습니다. 

 

  • Primary Type : 정수, 소수, 문자(char), 참/거짓, 
  • Advenced Type : 문자열(&str, String)
  • Component Type : Tuple, Array, Slice, ...
  • Collection Type : Vector, Map, Set, ...
  • Custom Type : Enum, Struct, ...

 

예제를 통해 어떻게 사용할 수 있는지 확인해 보겠습니다.

 

 


Code Example

Rust 1.79.0 (released 2024-06-13) 

fn main(){
    // =============================================
    // Primary type
    // =============================================
    
    /* integer type */
    let _i: i8 = 0;
    let _i: i16 = 1_000;        // underscore 사용 가능
    let _i: i32 = 32;           // 정수 기본 추론 type
    let _i: i64 = 0xff;         // 16진법, 8진법 등 다양한 기술을 지원.
    let _i: i128 = i128::MIN;   // Type 자체에서 가져와서 사용할 수 있다.
    let _i: isize = 0;          // os의 메모리 주소크기를 따름.


    /* unsigned integer type */
    let _u: u8 = b'a';          // char와 호환된다. Vec<u8>이 String과 같음을 기억하자.
    let _u: u16 = 0;
    let _u: u32 = 0;
    let _u: u64 = 0xFFFF_FFFF_FFFF_FFFF;    // u64::MAX와 같은표현. 
    let _u: u128 = u128::MAX;   // integer중 중 가장 큰 수;
    let _u: usize = 0;          // os의 메모리 주소크기를 따름.
    // 64bit OS에서는 64bit 메모리 주소를 가지므로 usize = u64


    /* float type */
    let _f: f32 = 0.0;
    let _f: f64 = 0.0;          // 실수 기본 추론 Type
    // let _f: f16 = 0.0;       // [ERROR] 지원하는 HW에서만 사용 가능
    // let _f: f128 = 0.0;      // [ERROR] 지원하는 HW에서만 사용 가능. (예: PowerPC)
    

    /* boolean type */
    let _b: bool = true;
    let _b: bool = false;
    let _c: char = 'c';         // ''는 한 글자 ascii에서 사용


    /* casting */
    let _num_int: i32 = 5/3;                // type이 동일해야 산술 연산 가능
    let _num_float: f64 = 5.0/3.0;          // type이 동일해야 산술 연산 가능
    // [Error] rust는 자동 형변환이 일어나지 않는다
    // let _num_div = 5/3.0;

    let _num_int: i32 = 5 / 3.0 as i32;     // as라는 keyword로 명시적인 casting을 지원.



    // =============================================
    // Advenced Type
    // =============================================

    /* &str Type */
    // &str : pointer, length(문자 수)를 가지는 immutable한 data.
    // String : pointer, length(문자 수), capacity(Memory 크기)를 가지는 mutable한 data.
    // &str은 주로 수정되지 않는 글자열을 다루는 String Slice. rust는 utf-8을 기본으로 한다.
    let _str: &str = "str";

    // 불가능한 문법은 아니지만 mutable한 속성을 이용하기 어렵다.
    // let mut _str = "test";


    /* String Type */
    // String은 Vec<u8> 형태의 객체 Type. 주로 수정될 것을 목적으로 한 문자열.
    let _string: String = String::from("string");
    // String 선언 방법 1.
    let _string = String::from("string");
    // String 선언 방법 2. 사실상 &str -> String 변환이다.
    let _string = "string".to_string();

    // String은 mutable하게 선언할 수 있다.
    let mut _string_mutable = String::from("mutable");
    _string_mutable.push_str(" add text");
    println!("_string_mutable : {_string_mutable}");  // mutable add text

    // String은 mutable하지만 mut를 선언한 변수만 변경 가능하다.
    let _string_immutable = String::from("immutable");
    // _string_immutable.push_str("test");  // [Error]



    // =============================================
    // Compound Types
    // =============================================

    /* Tuple type */
    // 길이와 type들이 고정된 단위 Data type
    let _t: (i32, f64, &str) = (1, 2.34, "567 number");
    // tuple에서 값을  분리해서 받을 수 있다.
    let (_t1, _t2, _t3) = _t;
    let mut _t_mut: (i32, i32) = (1, 2);    // mutable 선언
    _t_mut.0 = 3;           // Tuple indexing
    _t_mut.1 = 4;           // Tuple indexing

    // Compound Type을 print할 때는 {:?}, {:#?}같은 문법을 사용한다.
    println!("_t_mut : {_t_mut:?}");

    // [ERROR] rust는 tuple의 indexing을 variable로 할 수 없다.
    // const ADDRESS: usize = 1;
    // _t_mut.ADDRESS = -4;


    /* Array type */
    let _a: [i32; 5] = [1, 2, 3, 4, 5];    
    let _a = [1, 2, 3, 4, 5]; // type 생략해도 됨.
    println!("_a[0] : {}", _a[0]);      // Array indexing
    let address: usize = 3;
    println!("array : {_a:?}");

    // const를 활용한 변수 indexing은 가능하다. out of bound를 compiler time에 검사 가능
    println!("_a[address] : {}", _a[address]);

    let _a_repeat: [i32; 10] = [1; 10];             // rust의 array 반복 숫자 선언 방법
    println!("_a_repeat : {_a_repeat:?}");          // [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

    // [TIP] 1 ~ 10까지의 값을 뽑을 수 있음.
    let _a_increment:[usize; 10] = core::array::from_fn(|i| i + 1);
    println!("_a_increment : {_a_increment:?}");    // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    // 다양한 차원에 대해서 선언할 수 있다. 3 x 4 x 5로 이루어진 3차원의 1로 채워진 데이터.
    let mut _arr3d = [
        [
            [1; 3]; 4
        ]; 5
    ];  
    /*
    [
        [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]],
        [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]],
        [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]],
        [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]],
        [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]
    ]
    */
    println!("array : {_arr3d:?}");
    println!("array element : {}", _arr3d[4][3][2]);    // 다차원 array의 index 접근

    _arr3d[0] = [[2; 3]; 4];                            // 다차원 array insert
    println!("array : {_arr3d:?}");
    /*
    [
        [[2, 2, 2], [2, 2, 2], [2, 2, 2], [2, 2, 2]],
        [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]],
        [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]],
        [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]],
        [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]
    ]
    */

    /* Slice */
    let arr: [i32; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
    let slice: &[i32] = &arr[2..5];
    println!("slice : {slice:?}");      // [2, 3, 4]



    // =============================================
    // Collection Types
    // =============================================
    use std::collections::{HashMap, HashSet, BTreeMap, BTreeSet};

    /* Vector : 동적 크기 배열, runtime에 크기를 조정할 수 있음 */
    let mut _v:Vec<i32> = Vec::new();   // 가변 길이의 데이터 저장 Type인 빈 Vector 선언
    let mut v = Vec::new();   // push할 데이터 기반 Type 추론
    v.push(1);
    v.push(2);
    v.push(3);
    v.push(4);
    println!("{v:?}");

    // 가변 길이의 데이터 저장 Type인 빈 Vector 선언(macro version)
    let mut _v:Vec<i32> = vec![];

    // 가변 길이의 데이터 저장 Type인 데이터 포함 Vector 선언(macro version)
    let mut _v:Vec<i32> = vec![1, 2, 3, 4];
    

    /* HashMap : 중복되지 않는 key와 value pairs로 데이터를 저장하는 구조, unordered */
    let mut _h_map: HashMap<&str, i32> = HashMap::new();    // 순서 없는 key, value 기반 data type
    let mut h_map = HashMap::new();    // push할 데이터 기반 Type 추론

    // insert
    h_map.insert("key1", 1);
    h_map.insert("key2", 2);
    h_map.insert("key3", 3);

    // overwrite
    h_map.insert("key1", 11);  // 중복된 키 "key1"에 삽입 -> overwrite
    
    // get
    h_map.get("no_key");    // None
    h_map.get("key2");      // Some(2)

    // update
    // key가 없으면 update 동작을 하지 않는다.
    if let Some(value) = h_map.get_mut("no_key") { *value += 10; }

    // key가 있다면 value를 받아와 update한다.
    if let Some(value) = h_map.get_mut("key3") { *value *= 10; }    // 30

    // remove. pop처럼 꺼내면서 값을 출력한다.
    println!("key2 : {:?}", h_map.remove("key2"));   // pop과 같은 동작. Some(2)
    println!("key2 : {:?}", h_map.remove("key2"));   // 없는 key에 pop을 하면 None을 얻는다.

    // check key
    println!("key 1 contained : {}", h_map.contains_key("key1"));
    println!("no key contained : {}", h_map.contains_key("no_key"));

    // size
    println!("Number of elements: {}", h_map.len());

    // clear (모든 키, 값 삭제)
    h_map.clear();
    println!("Number of elements after clear: {}", h_map.len()); // 0



    /* Hashset : 중복되지 않는 값을 저장하는 구조. unordered */
    let mut _h_set: HashSet<&str> = HashSet::new();          // 순서 없는 값 기반 data type
    let mut h_set = HashSet::new();    // push할 데이터 기반 Type 추론

    // insert
    h_set.insert("apple");
    h_set.insert("banana");
    h_set.insert("cherry");

    // 중복된 값 추가 시 삽입되지 않음
    h_set.insert("apple");  // "apple"은 이미 존재하므로 삽입되지 않음

    // contains (값이 존재하는지 확인)
    println!("Contains 'banana': {}", h_set.contains("banana")); // true
    println!("Contains 'grape': {}", h_set.contains("grape"));   // false

    // remove. 값이 있었다면 true, 없었다면 false
    println!("cherry : {}", h_set.remove("cherry")); // true, 값이 삭제됨
    println!("cherry : {}", h_set.remove("cherry")); // false, 값이 존재하지 않음

    // size
    println!("Number of elements: {}", h_set.len());  // 2 ("apple", "banana")

    // clear (모든 값 삭제)
    h_set.clear();
    println!("Number of elements after clear: {}", h_set.len()); // 0

    // Iterating through HashSet
    h_set.insert("one");
    h_set.insert("two");
    h_set.insert("three");

    for value in &h_set {
        println!("{}", value);  // "one", "two", "three" (순서 없음)
    }

    /* BtreeMap : HashMap에서 Key, Value를 순서대로 정렬된 상태를 유지하는 data 구조 */
    let mut btree_map = BTreeMap::new();
    btree_map.insert(3, "three");
    btree_map.insert(1, "one");
    btree_map.insert(2, "two");

    // 순서대로 키-값 출력
    for (key, value) in &btree_map {
        println!("{}: {}", key, value);
    }

    // 첫 번째, 마지막 값
    if let Some((key, value)) = btree_map.iter().next() {
        println!("First: {}: {}", key, value);
    }
    if let Some((key, value)) = btree_map.iter().rev().next() {
        println!("Last: {}: {}", key, value);
    }

    /* BtreeSet : HashSet에서 Value를 순서대로 정렬된 상태를 유지하는 data 구조 */
    // BTreeSet 예시
    let mut btree_set = BTreeSet::new();
    btree_set.insert(3);
    btree_set.insert(1);
    btree_set.insert(2);

    // 순서대로 값 출력
    for value in &btree_set {
        println!("{}", value);
    }

    // 첫 번째, 마지막 값
    if let Some(first) = btree_set.iter().next() {
        println!("First: {}", first);
    }
    if let Some(last) = btree_set.iter().rev().next() {
        println!("Last: {}", last);
    }

    // 범위 출력
    let range: Vec<_> = btree_set.range(1..3).collect();
    println!("Range (1..3): {:?}", range);
    
    // =============================================
    // Custom Types
    // =============================================

    /* Struct type : type들의 집합. field에 이름이 있음 */
    struct _Point {
        x: i32,
        y: i32,
    }

    let _point = _Point {x: 10, y: 10};
    // [Error] error[E0277]: `_Point` doesn't implement `Debug`
    // println!("point : {point:?}"); // struct는 그냥 print할 수 없다.
    println!("(x, y) : ({}, {})", _point.x, _point.y);

    // struct를 print하려면 Debug trait을 적용해야 한다.
    #[derive(Debug)]
    struct Point {
        x: i32,
        y: i32
    }

    let point = Point {x: 10, y: 10};
    println!("point : {point:?}");
    println!("(x, y) : ({}, {})", point.x, point.y);


    /* Enum : 열거형. 값은 u8기준으로 0부터 들어간다. 임의로 넣을 수 있으며 다른 정수 type도 가능하다. */
    #[derive(Debug)]
    enum Direction {
        Up,             // 0
        Down,           // 1
        Left,           // 2
        Right=30,       // value도 따로 넣을 수 있다.
        // double=true, // [Error] boolean type은 안된다. 정수형만 허용
    }

    let up = Direction::Up;
    let down = Direction::Down;
    let left = Direction::Left;
    let right = Direction::Right;
    println!("{up:?}, {down:?}, {left:?}, {right:?}");     // Up, Down, Left, Right

    // 값을 출력하고 싶다면 casting을 사용하면 된다.
    println!("up value : {}", up as u8);        // up value : 0
    println!("right value : {}", right as u8);  // right value : 30
}

 

 


 

* reference

 - https://doc.rust-lang.org/book/

 

'Rust > Basic' 카테고리의 다른 글

[Rust Tutorial] 6 - Control Flow  (0) 2024.11.17
[Rust Tutorial] 5 - Function  (0) 2024.11.17
[Rust Tutorial] 3 - Variables  (0) 2024.11.14
[Rust Tutorial] 2 - Cargo 사용법  (0) 2024.11.14
[Rust Tutorial] 1 - Introduce  (0) 2024.11.13