데이터 타입

Rust는 기본적으로 어떤 타입인지 명시하여 데이터를 다룬다. 타입은 크게 스칼라와 컴파운드로 나눈다.

Rust는 타입이 고정된 언어로 모든 변수의 타입이 컴파일 시에 반드시 정해져 있어야 한다. 컴파일러가 타입을 추측할 수 있지만 형변환 시 타입의 선택 폭이 넓은 경우 반드시 타입 명시를 첨가해야 한다.

예를 들어 Stringparse하여 숫자로 변환했을 경우

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

타입을 명시하지 않을 경우 Rust는 에러를 발생시킨다.

error[E0282]: type annotations needed
 --> src/main.rs:2:9
  |
2 |     let guess = "42".parse().expect("Not a number!");
  |         ^^^^^
  |         cannot infer type for `_`

스칼라 타입

하나의 값으로 표현되는 타입을 말한다. 정수형, 부동 소수점, boolean, 문자 타입이 스칼라 타입이다.

정수형

소수점 없는 숫자이다.

8bit i8 u8
16bit i16 u16
32bit i32 u32
64bit i64 u64
arch isize usize

정리하면 다음과 같다.

  • 대부분 프로그래밍 언어가 그렇듯이, 음수의 경우 2의 보수 형태로 저장된다.
  • isize & usize 타입은 프로그램이 동작하는 컴퓨터 환경이 64bit or 32bit 환경인지에 따라 결정된다.
  • 정수 리터럴 접미사 허용
  • 기본값은 i32 로 모든 환경에서 빠르며, 확실히 정해진 경우가 아니면 좋은 선택이다.

부동 소수점

실수 표현은 부동 소수점으로 두 가지 타입이 있다.

32bit f32
64bit f64

기본 타입은 f64 이유는 최신 CPU 상에서는 f32 와 대략 비슷한 속도를 내면서 더 정밀한 표현이 가능하다.(단, 그 만큼 메모리 공간 차지)

부동 소수점은 IEEE-754 표준에 따라 표현한다. 표준 구현 특성상 많은 공간을 차지하는 f64 가 정밀도가 더 높다.(f32는 1배수의 정밀도, f64는 2배수의 정밀도)

수학적 연산

기본적인 사칙연산은 다음과 같다.

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

    // subtraction
    let difference = 95.5 - 4.3;

    // multiplication
    let product = 4 * 30;

    // division
    let quotient = 56.7 / 32.2;

    // remainder
    let remainder = 43 % 5;
}

boolean

truefalse 이다. bool 로 명시

fn main() {
    let t = true;

    let f: bool = false; // with explicit type annotation
}

문자

String이 큰따옴표를 쓰는 것에 반하여 char 는 언어의 가장 근본적인 알파벳 타입이다.(또한 많은 표현이 가능, 한국어/중국어/일본어/이모티콘/공백 등)

컴파운드 타입

컴파운드(복합) 타입은 다른 타입의 다양한 값들을 하나의 타입으로 묶어 사용할 수 있다.

튜플

여러 타입의 집합을 말한다. 타입 명시는 선택 사항이다.

fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
}

튜플은 단일(스칼라 타입) 요소를 위한 복합계로 고려되었기 때문에 tup 에는 튜플 전체가 바인드된다. 다음과 같이 개별적으로 값을 추출할 수 있다.

패턴 매칭을 통한 값 추출

fn main() {
    let tup = (500, 6.4, 1);

    let (x, y, z) = tup;

    println!("The value of y is: {}", y);
}

색인을 통한 값 추출

fn main() {
    let x: (i32, f64, u8) = (500, 6.4, 1);

    let five_hundred = x.0;

    let six_point_four = x.1;

    let one = x.2;
}

배열

배열은 튜플과 다르게 모든 요소가 같은 타입이여야 한다. 배열이 유용할 때는 데이터를 heap보다 stack에 할당하거나, 항상 고정된 요소를 같는 집합에 유용하다. 예를 들면 달력이 있다.

let months = ["January", "February", "March", "April", "May", "June", "July",
              "August", "September", "October", "November", "December"];

Rust의 배열이 다른 언어의 배열과 다른 점은 고정된 길이를 갖는다는 점이다. 이는 배열의 끝을 넘어선 요소에 접근하려고 한다면 다음과 같은 에러가 난다.

$ cargo run
   Compiling arrays v0.1.0 (file:///projects/arrays)
    Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
     Running `target/debug/arrays`
thread '<main>' panicked at 'index out of bounds: the len is 5 but the index is
 10', src/main.rs:6
note: Run with `RUST_BACKTRACE=1` for a backtrace.

컴파일 시 에러가 발생하지 않지만, 프로그램 결과는 실행 중에 에러가 발생했고 성공적으로 종료되지 못했다고 나온다. 이처럼 프로그램 오류와 함께 종료될 때 Rust에서는 “패닉(Panic)” 상태라고 표현한다.

이것은 Rust의 안전 원칙이 동작하는 첫 번째 예로, 많은 저수준 언어에서 이러한 타입의 검사는 수행되지 않으며 잘못된 색인을 제공하면 잘못된 메모리에 접근할 수 있다. Rust는 메모리 접근을 허용하는 대신 즉시 종료하여 이러한 종류의 오류로부터 사용자를 보호한다.

const arr = [1, 2, 3];
console.log(arr[3]); // undefined