Programming

[RUST언어] RUST 언어의 시작 (2) - 변수 타입의 종류

IT오이시이 2023. 5. 16. 16:17
728x90

 

RUST 언어의 변수 타입의 종류

Rust 언어에서는 다양한 변수 타입을 제공합니다. 다음은 Rust에서 사용되는 주요 변수 타입은 다음과 같습니다:



1)  기본 타입 (Primitive types):

Rust는 몇 가지 기본 타입을 제공합니다. 예를 들어, i32는 32비트 부호 있는 정수를 나타내며, f64는 64비트 부동 소수점 수를 나타냅니다. 다른 기본 타입으로는 부호 없는 정수(u8, u16, u32, u64)와 부동 소수점(f32) 등이 있습니다. 이러한 기본 타입은 숫자, 문자, 부울 등을 표현하는 데 사용됩니다.

■ 정수형 (Integer types):
부호 있는 정수: i8, i16, i32, i64, i128
부호 없는 정수: u8, u16, u32, u64, u128

예시: let x: i32 = 42;


■ 부동 소수점형 (Floating-point types):
32비트 부동 소수점: f32
64비트 부동 소수점: f64

예시: let pi: f64 = 3.14159;


■ 부울형 (Boolean type):
bool 타입은 true 또는 false 값을 가집니다.

예시: let is_true: bool = true;


■  문자형 (Character type):
  char 타입은 단일 유니코드 문자를 나타냅니다.

예시: let c: char = 'A';

 

 

2) 문자열 타입 (String types):

 

`String` 타입은 동적으로 크기가 조정될 수 있는 문자열을 나타냅니다.
Rust에서 문자열은 String 타입과 문자열 슬라이스(&str) 타입으로 나타낼 수 있습니다. String 타입은 동적으로 크기가 조정될 수 있는 문자열을 표현하고, 문자열 슬라이스는 문자열의 일부분을 참조하는 불변 참조자입니다.

let greeting: String = String::from("Hello, Rust!");

 

 

3) 배열 타입 (Array types):



Rust의 배열은 고정된 크기를 가지며 동일한 타입의 요소로 이루어진 타입입니다. 배열은 스택에 할당되며, 크기와 요소의 타입이 컴파일 시점에 알려져야 합니다.

let array: [i32; 3] = [1, 2, 3];

 

 

4) 튜플 타입 (Tuple types):

튜플은 서로 다른 타입의 요소를 가질 수 있는 복합 타입입니다. 튜플은 고정된 크기를 가지며, 요소에는 인덱스를 사용하여 접근할 수 있습니다.

let tuple: (i32, f64, bool) = (42, 3.14, true);

 

 

5) 벡터 타입 (Vector types):

Rust에서 벡터 타입은 동적으로 크기가 조정될 수 있는 배열을 나타냅니다.  벡터는 동적으로 크기가 조정될 수 있는 배열로, Vec<T> 타입으로 표현됩니다. 벡터는 힙에 할당되며, 요소의 타입은 동일해야 합니다.

벡터 타입의 특징 
1) 동적 크기: 벡터는 동적으로 크기가 조정될 수 있습니다. 요소를 추가하거나 제거함으로써 크기를 변경할 수 있습니다.

2) 소유권과 이동: 벡터는 요소의 소유권을 가집니다. 따라서 벡터에 요소를 추가하면 해당 요소의 소유권이 벡터로 이전됩니다.

3) 인덱싱: 벡터의 요소에는 0부터 시작하는 인덱스를 사용하여 접근할 수 있습니다. 예를 들어, vector[0]과 같이 사용합니다.

4) 다양한 메서드: 벡터는 다양한 메서드를 제공하여 요소를 추가, 제거, 수정하고, 검색 및 정렬 등의 작업을 수행할 수 있습니다. 예를 들어, push() 메서드를 사용하여 요소를 벡터에 추가할 수 있습니다.

 

벡터에서 제공하는 메서드

1. new(): 빈 벡터를 생성합니다.
2. push(): 요소를 벡터의 끝에 추가합니다.
3. pop(): 벡터의 마지막 요소를 제거하고 반환합니다.
4. len(): 벡터의 현재 요소 개수를 반환합니다.
5. is_empty(): 벡터가 비어있는지 여부를 확인합니다.
6. contains(): 특정 요소가 벡터에 존재하는지 확인합니다.
7. sort(): 벡터의 요소를 정렬합니다.
8. iter(): 벡터의 요소를 순회하기 위한 반복자를 생성합니다.

 

벡터타입의 활용

  `use std::vec::Vec;` 와 같이 벡터를 사용하고자 하는 파일 상단에 벡터 타입을 선언하여 사용 합니다.

use std::vec::Vec;

fn main() {
    let mut numbers: Vec<i32> = Vec::new(); // 비어있는 벡터 생성

    numbers.push(1);
    numbers.push(2);
    numbers.push(3);

    println!("{:?}", numbers); // 출력: [1, 2, 3]

    let first = numbers[0];
    println!("First element: {}", first); // 출력: 1

    numbers.pop();
    println!("{:?}", numbers); // 출력: [1, 2]
}

 

위의 예시에서는 Vec::new()를 사용하여 비어있는 벡터를 생성하고, push()를 사용하여 요소를 추가합니다. 인덱싱을 통해 요소에 접근하고, pop()을 사용하여 마지막 요소를 제거합니다.

 

6) 구조체 (Structs):

Rust에서 구조체(Structs)는 관련된 데이터를 묶어 하나의 사용자 정의 데이터 타입을 생성하는 데 사용됩니다. 구조체는 다양한 타입의 필드로 구성되며, 각 필드는 이름과 해당 필드가 가지는 타입을 가지고 있습니다. 

구조체는 데이터의 그룹화와 구조적인 표현을 가능하게 하여 코드를 더욱 구조화하고 가독성을 높이는 데 도움을 줍니다

예시) 구조체를 정의하기 위해서는 struct 키워드로 선언하여 사용합니다.

// 구조체 정의
struct Person {
    name: String,
    age: u32,
    is_student: bool,
}

fn main() {
    // 구조체 인스턴스 생성
    let person1 = Person {
        name: String::from("Alice"),
        age: 25,
        is_student: true,
    };

    // 구조체 필드 접근
    println!("Name: {}", person1.name);
    println!("Age: {}", person1.age);
    println!("Is Student: {}", person1.is_student);
}

 

위의 예시에서는 Person이라는 구조체를 정의했습니다. 구조체는 name (문자열), age (부호 없는 32비트 정수), is_student (부울 값)와 같은 세 개의 필드를 가지고 있습니다. 이후 main 함수에서 Person 구조체의 인스턴스인 person1을 생성하고 필드에 값을 할당합니다. 마지막으로, println! 매크로를 사용하여 구조체의 필드에 접근하여 값을 출력합니다.

 

 

7) 열거형 (Enums):

열거형(Enums)은 서로 다른 타입의 값들을 나타내는 사용자 정의 데이터 타입입니다. 각 값은 열거형의 다른 변형(variant)에 대응됩니다.

열거형은 코드의 가독성을 높이고, 정확한 값의 제한을 강제하며, 다양한 상황에 대응하기 위해 유용하게 사용됩니다. 

 열거형은 몇 개의 가능한 값 중 하나를 가질 수 있는 변수를 표현할 때 유용합니다. 예를 들어, 여러 상태 또는 옵션 중 하나를 나타내는 경우에 사용됩니다.

 

[열거형의 특징]

1) 다양한 가지(branches): 열거형은 여러 개의 가지를 가질 수 있습니다. 각 가지는 고유한 값을 나타냅니다.

2) 패턴 매칭(Pattern matching): 열거형의 가지에 따라 동작을 분기하기 위해 패턴 매칭을 사용할 수 있습니다. 이를 통해 다양한 상황에 대한 대응이 가능합니다.

3) 연관 데이터(Associated data): 각 가지는 연관 데이터를 가질 수 있습니다. 이는 해당 가지의 값을 더 세부적으로 설명하거나 추가 정보를 저장하는 데 사용됩니다.

 

열거형을 정의하기 위해서는 enum 키워드를 사용합니다. 각 가지는 쉼표로 구분되며, 각 가지에는 선택적으로 연관 데이터를 추가합니다.

enum TrafficLight {
    Red,
    Yellow,
    Green,
}

fn main() {
    let light = TrafficLight::Red;

    match light {
        TrafficLight::Red => println!("Stop!"),
        TrafficLight::Yellow => println!("Caution!"),
        TrafficLight::Green => println!("Go!"),
    }
}

 

위의 예시에서는 TrafficLight라는 열거형을 정의했습니다. 열거형은 Red, Yellow, Green이라는 세 가지 가지를 가지고 있습니다. main 함수에서는 light 변수를 TrafficLight::Red로 초기화하고, match 표현식을 사용하여 light의 값을 분기하여 해당하는 동작을 수행합니다.

 

8) 트레이트 객체 (Trait Objects):

 

Rust에서 트레이트 객체(Trait Objects)는 다양한 타입의 객체를 다룰 수 있는 일종의 동적 다형성을 제공하는 기능입니다. 트레이트 객체는 트레이트를 구현한 타입의 값을 가리키는 포인터로, 실행 시간에 타입을 알 수 없는 상황에서도 트레이트의 메서드를 호출할 수 있게 합니다.

트레이트 객체는 동적 디스패치를 통해 다양한 타입의 값을 다루는 데 사용됩니다. 트레이트 객체는 dyn Trait 형태로 나타내며, 해당 트레이트를 구현한 타입의 값을 가질 수 있습니다.

1) 트레이트 정의: 트레이트를 정의하고 해당 트레이트에 필요한 메서드를 선언합니다.

trait Drawable {
    fn draw(&self);
}



2) 구조체 정의와 트레이트 구현: 트레이트를 구현하는 구조체를 정의하고, 해당 트레이트의 메서드를 구현합니다.

struct Circle {
    radius: f64,
}

impl Drawable for Circle {
    fn draw(&self) {
        println!("Drawing a circle with radius: {}", self.radius);
    }
}



3) 트레이트 객체 사용: 트레이트 객체를 사용하여 다형성을 활용합니다. 트레이트 객체는 Box<dyn Trait> 형태로 사용되며, 해당 트레이트를 구현한 구조체의 인스턴스를 가리킵니다.

fn main() {
    let circle: Box<dyn Drawable> = Box::new(Circle { radius: 5.0 });

    circle.draw(); // 구체적인 타입을 모르지만 Drawable 트레이트의 draw 메서드 호출 가능
}

 
위의 예시에서는 Drawable 트레이트를 정의하고, Circle 구조체에서 Drawable 트레이트를 구현했습니다. main 함수에서는 Box<dyn Drawable>을 사용하여 트레이트 객체를 생성하고, draw 메서드를 호출합니다. 이때 구체적인 타입을 알지 못하지만, 트레이트 객체를 통해 메서드를 호출할 수 있습니다.

트레이트 객체를 사용하면 런타임 다형성을 구현할 수 있어, 유연성과 확장성을 제공합니다. 다른 타입의 객체를 하나의 트레이트 객체 컨테이너에 넣어 공통 동작을 수행할 수 있습니다. 이는 다양한 타입을 다루어야 하는 상황에서 유용하며, 특히 다형적 컬렉션 또는 동적 디스패치가 필요한 상황에서 효과적입니다.

 

 

9) 포인터 타입 (Pointer types):

 

Rust에는 몇 가지 유형의 포인터 타입이 있습니다. 포인터 타입은 메모리 주소를 가리키는 값을 나타내며, 메모리 상의 데이터에 접근하는 데 사용됩니다.

포인터는 메모리 주소를 나타내며, 특정 타입의 값을 가리키는 데 사용됩니다. Rust는 공유 참조자(&T), 가변 참조자(&mut T), Raw 포인터(*const T, *mut T) 등 다양한 포인터 타입을 제공합니다.

Rust의 포인터 타입은 안전성과 성능을 위해 다양한 규칙과 제한을 따릅니다. 특히, 빌림 규칙을 통해 Rust는 데이터 경합 및 메모리 안전성 문제로부터 보호됩니다. 안전하지 않은 Rust에서는 Raw 포인터를 사용하여 메모리에 직접 접근할 수 있지만, 이는 주의가 필요한 기능입니다. 

 

다양한 포인터 타입 유형

rust 다양한 포인터 타입을 사용합니다. raw_ptr와 mutable_raw_ptr은 Raw 포인터를 나타내며, immutable_ref와 mutable_ref는 References를 나타냅니다. boxed_value는 Box 포인터를 사용하고, slice는 슬라이스 포인터를 사용합니다.

fn main() {
    // Raw Pointers
    let raw_ptr: *const i32 = &10 as *const i32;
    let mut mutable_raw_ptr: *mut i32 = &mut 20 as *mut i32;

    // References
    let x: i32 = 30;
    let immutable_ref: &i32 = &x;
    let mut mutable_ref: &mut i32 = &mut x;

    // Box Pointers
    let boxed_value: Box<i32> = Box::new(40);

    // Slice Pointers
    let array: [i32; 5] = [1, 2, 3, 4, 5];
    let slice: &[i32] = &array[1..4];

    // Accessing values
    unsafe {
        println!("Raw Pointer: {}", *raw_ptr);
        println!("Mutable Raw Pointer: {}", *mutable_raw_ptr);
    }

    println!("Immutable Reference: {}", *immutable_ref);
    println!("Mutable Reference: {}", *mutable_ref);
    println!("Boxed Value: {}", *boxed_value);
    println!("Slice: {:?}", slice);
}

 

 

1) Raw Pointers:

*const T: 불변(raw) 포인터로, 가리키는 값이 변경되지 않음을 나타냅니다.
*mut T: 가변(raw) 포인터로, 가리키는 값이 변경될 수 있음을 나타냅니다.
Raw 포인터는 안전하지 않은(Unsafe) Rust의 기능으로, 명시적인 안전 조치 없이도 메모리에 접근할 수 있게 합니다. 이는 Rust에서 메모리 누수, 데이터 경합 및 다른 안전 문제를 발생시킬 수 있는 위험한 기능입니다. 주로 FFI(Foreign Function Interface)와 저수준 작업에 사용됩니다.

 

2) References Pointers:

&T: 불변(reference) 포인터로, 가리키는 값에 대한 불변 참조를 나타냅니다.
&mut T: 가변(reference) 포인터로, 가리키는 값에 대한 가변 참조를 나타냅니다.
References는 Rust의 핵심 개념 중 하나로, 소유권과 빌림(Borrowing) 규칙에 기반하여 메모리 안전성을 보장합니다. References는 안전한 코드를 작성하는 데 도움을 주며, 복사보다 경제적인 데이터 접근 방식을 제공합니다.

 

3) Box Pointers:

Box<T>: 힙(heap)에 할당된 값에 대한 포인터로, 소유권을 가지는 스마트 포인터입니다.
Box<T>는 단일 스레드에서 사용되는 간단한 포인터로, 힙 메모리에 할당된 값의 소유권을 가집니다. Box<T>는 자동으로 메모리를 해제하며, 스택에 대신 힙 메모리에 데이터를 저장함으로써 크기가 동적인 데이터를 처리하는 데 도움을 줍니다.

4) Slice Pointers:

&[T]: 배열의 슬라이스를 나타내는 포인터로, 배열의 일부분을 참조합니다.
슬라이스 포인터는 배열의 일부를 참조하며, 해당 부분에 대한 길이와 시작 위치를 저장합니다. 슬라이스는 배열의 원소에 대한 뷰를 제공하여 쉽게 데이터를 조작하고 검색할 수 있도록 합니다.

 


Rust는 강력한 타입 시스템을 갖추고 있으며, 변수 타입의 명시성과 안정성을 강조합니다. 변수의 타입을 명시적으로 지정하거나, 컴파일러가 타입을 추론하도록 할 수 있습니다. 이러한 기능을 통해 Rust는 안전한 프로그래밍을 지원하며, 메모리 안전성과 스레드 안전성을 보장합니다.​

 

728x90
반응형