[Rust] 데이터 타입(1) - 스칼라 타입

RedBean·2024년 7월 20일
1

Rust 배우기

목록 보기
2/4
post-thumbnail

스칼라 타입

러스트의 모든 값들은 특정한 타입을 가지며, 이는 러스트가 해당 데이터로 작업하는 방법을 알 수 있도록 알려준다. 여기서는 타입을 스칼라 타입과 복합 타입, 두 가지로 나눌 것이다. 이 중 스칼라 타입을 오늘 다룰 것이다.

러스트는 정적 타입 언어이다. 이것이 의미하는 바는 모든 변수의 타입이 컴파일 시점에 정해져 있어야 한다는 것이다. 보통 컴파일러는 우리가 값을 어떻게 사용하는지에 따라 타입을 추측할 수 있다.

let guess: u32 = "42".parse().expect("Not a number!");

이런 경우처럼 여러 가지 타입이 가능한 경우는 위의 u32처럼 타입 명시를 추가해야 한다.

여기에 : u32라는 타입 명시를 하지 않으면 컴파일러는 타입에 대한 정보가 부족하다고 에러를 출력한다.

$ cargo build
...
error[E0282]: type annotations needed
...

이제 다양한 데이터 타입들의 타입 명시를 알아보자.

스칼라 타입

스칼라 타입은 하나의 값을 표현한다.
러스트는 정수, 부동 소수점 숫자, 불리언(boolean), 문자, 이렇게 네 가지 스칼라 타입을 가지고 있다. 다른 프로그래밍 언어들에서도 구현되어 있는 사항이니 익숙할 것이다.

정수형

정수형(integer type)은 소수점이 없는 숫자다.
다음은 러스트에서 사용되는 정수형들이다.

러스트의 정수형 타입들

길이부호 있음 (signed)부호 없음 (unsigned)
8-biti8u8
16-biti16u16
32-biti32u32
64-biti64u64
128-biti128u128
archisizeusize

각각의 타입은 부호 있는(signed) 혹은 부호 없는(unsigned) 타입이며 명시된 크기를 가진다. 부호 혹은 부호 없음의 의미는 타입이 음수를 다룰 수 있는지를 나타낸다.

각 부호 있는 타입의 변수는 n이 비트 수일 때

(2n1)2n11-(2^{n-1})\sim2^{n-1}-1

까지의 값을 저장할 수 있고,

부호 없는 타입의 변수는

02n110\sim2^{n-1}-1

까지의 값을 저장할 수 있다.

예시로, u8타입은 0에서 255까지의 값을 저장할 수 있다.

isize, usize 타입

프로그램이 동작하는 컴퓨터 환경에 따라 결정되는데,
위 테이블에는 ‘arch’라고 적시되어 있다.
64-bit 아키텍처이면 64비트를,
32-bit 아키텍처이면 32비트를 갖게 된다.
보통 이는 어떤 컬렉션 종류의 인덱스에 사용된다.

그렇다면 실질적으로 정수형을 나타내려면 어떻게 해야 하는가? 정수형 리터럴을 사용해서 작성하면 된다.
정수형 리터럴은 코드 내에서 직접적으로 표현되는 정수 값을 나타낸다.
정수형 리터럴은 다음과 같은 형태로 작성할 수 있다.

숫자 리터럴
Decimal(10진수)98_222
Hex (16진수)0xff
Octal (8진수)0o77
Binary (2진수)0b1111_0000
Byte (u8만 가능)b'A'

그렇다면 어떤 타입의 정수를 사용해야 하는지는 어떻게 알아낼까?
확실히 정해진 경우가 아니라면 러스트의 기본값인 i32타입을 사용하는 것이 좋다.

정수 오버플로우란?

긴 글 주의 (펼치기/접기) 0과 255 사이의 값을 담을 수 있는 u8 타입의 변수를 가지고 있다고 해보자. 만약에 이 변수를 256처럼 정해진 범위 밖의 값으로 변경하려고 하면 **정수 오버플로우**가 일어나는데, 이는 둘 중 한가지의 동작을 일으킨다. 코드를 디버그 모드에서 컴파일 할 경우, 런타임에 정수 오버플로우가 발생했을 때 `패닉(panic)`을 발생시키는 검사를 포함시킨다. 여기서 패닉이란 에러가 발생하면서 프로그램이 종료되는 경우를 말한다. 이후 이에 대해 다룰 것이다.

--release 태그를 사용해서 코드를 릴리즈 모드로 컴파일하는 경우,
러스트는 오버플로우 검사를 실행 파일에 포함시키지 않는다.
대신 오버플로우가 발생하면 러스트는 2의 보수 감싸기(two's complement wrapping을 수행한다.
어떤 식이냐면, 위에서 말한 u8 변수에 256을 저장하려고 하면 0이, 257은 1이 되는 방식이다.
프로그램은 패닉을 발생시키지 않으나, 원하는 방식으로 프로그램이 작동하지 않을 것이기 때문에 이는 에러로 간주된다.

명시적으로 오버플로우의 가능성을 다루기 위해서는 표준 라이브러리에서 기본 수치 타입에 대해 제공하는 아래 메서드 종류들을 사용할 수 있다.

  • wrapping_add와 같은 wrapping_* 메서드로 감싸기 동작 실행하기
  • checked_* 메서드를 사용하여 오버플로우가 발생하면 None 값 반환하기
  • overflowing_* 메서드를 사용하여 값과 함께 오버플로우 발생이 있었는지를 알려주는 부울린 값 반환하기
  • saturating_* 메서드를 사용하여 값의 최대 혹은 최솟값 사이로 제한하기

부동 소수점 타입

러스트에도 부동 소수점(파이썬에서의 float) 숫자 기본 타입이 2가지 있다.
f32f64로, 각각 32bit와 64bit의 크기를 가진다.

기본 타입은 f64인데, 그 이유는 현대 CPU에서 f32와 비슷한 속도로 더 정밀하게 작업할 수 있기 때문이다.

또한 모든 부동 소수점 타입은 부호가 있다.

fn main() {
    let x = 2.0; // f64

    let y: f32 = 3.0; // f32
}

수치 연산

러스트는 모든 숫자 타입에 대해서 여러분이 예상할 수 있는 기본 수학 연산 기능을 제공한다. 정수 나눗셈은 가장 가까운 정숫값으로 버림한다.

다음은 수치 연산의 예시 코드이다.

fn main() {
    // 덧셈
    let sum = 5 + 10;

    // 뺄셈
    let difference = 95.5 - 4.3;

    // 곱셈
    let product = 4 * 30;

    // 나눗셈
    let quotient = 56.7 / 32.2;
    let truncated = -5 / 3; // 결괏값은 -1입니다

    // 나머지 연산
    let remainder = 43 % 5;
}

여기서 러스트가 제공하는 모든 연산자 목록을 확인할 수 있다.

불리언 타입

대부분의 다른 언어들처럼, 러스트에서도 불리언은 true와 false 두 값을 가질 수 있다. 불리언 값은 1바이트다. bool로 명시된다.

fn main() {
    let t = true;

    let f: bool = false; // 명시적인 타입 어노테이션
}

이것을 사용하는 주요 방식은 if와 같은 조건문에서 사용하는 것이다. 이는 추후 다룰 것이다.

문자 타입

러스트의 char는 이 언어의 가장 기본적인 알파벳 타입이다.

다음은 char 타입의 예시이다.

fn main() {
    let c = 'z';
    let z: char = 'ℤ'; // 명시적인 타입 어노테이션
    let heart_eyed_cat = '😻';
}

문자열 리터럴과의 차이점은 문자열 리터럴은 "를 사용하고, char'를 사용한다는 것이다.
러스트의 char 타입은 4바이트 크기이며 유니코드 스칼라 값을 표현할 수 있다. 또한 한국어, 억양 표시 문자, 중국어, 일본어, 이모지, 공백 문자까지도 러스트에서는 char 값으로 사용이 가능하다.

다음 글에서는 복합 타입에 대해 다룰 것이다.

profile
Rust를 배우고 있는

0개의 댓글

관련 채용 정보