Rust 기초: 원시 자료형(Primitives) 정리
어떤 프로그래밍 언어를 학습하든 Primitives, 즉 원시 자료형에 대한 이해은 매우 중요합니다. 나중에 배우게 될 struct과 enum과 같은 커스텀(Custom) 자료형의 근간이 되는 중요한 개념이기 때문입니다.
이번 글에서는 Rust의 주요 원시 자료형들을 살펴보고, 각 타입이 어떤 특성을 가지는지, 그리고 실제로 어떻게 사용하는지 예제를 통해 알아보겠습니다.
원시 자료형이란?
Rust의 원시 자료형은 언어에 내장되어 있는 가장 기본적인 데이터 유형(type)입니다. 변수나 함수 선언 시 명시적으로 자료형을 지정할 수도 있고, 컴파일러가 자동으로 추론하기도 합니다.
Rust의 주요 원시 자료형은 아래와 같습니다.
- 정수형(
u16
,i32
등) - 부동소수점형(
f32
,f64
) - 불리언형:
bool
- 문자형:
char
- 튜플형:
(i32, bool)
- 배열형:
[i32; 5]
Rust는 성능과 안정성을 모두 추구하는 시스템 프로그래밍 언어입니다. 이를 위해 메모리 레이아웃이 명확하고, 타입 시스템이 엄격하게 설계가 되어 있죠.
정수형 (Integers)
Rust는 부호 개념이 있는 정수(Signed)와 부호 개념이 없는 정수(Unsigned)를 명확히 구분합니다.
Signed 정수형은 i
뒤에 비트 수가 붙고, Unsigned 정수형은 u
뒤에 비트 수가 붙습니다.
예를 들어, i8
은 8비트로 이루어진 Signed 정수형이며, -2^7 (=-128)
부터 2^7 - 1 (=127)
까지 표현할 수 있습니다.
반면에, u8
은 8비트로 이루어진 Unsigned 정수형이며, 0
부터 2^8 - 1 (=255)
까지 표현할 수 있습니다.
동일한 개수의 비트를 가지고 표현할 수 있는 수의 개수는 256개로 동일하나, 음수 개념이 없는 Unsigned 자료형으로는 Signed 자료형보다 2배 큰 양수를 표현할 수 있는 것을 알 수 있습니다.
크기에 따라 8비트, 16비트, 32비트, 64비트 정수 자료형이 존재합니다. 비트 수가 클수록 더 큰 범위를 표현할 수 있지만, 그만큼 많은 양의 메모리를 소모합니다.
자료형 | 부호 | 크기 | 범위 |
---|---|---|---|
i8 |
✅ | 8비트 | -128 ~ 127 |
u8 |
❌ | 8비트 | 0 ~ 255 |
i16 |
✅ | 16비트 | -32,768 ~ 32,767 |
u16 |
❌ | 16비트 | 0 ~ 65,535 |
i32 |
✅ | 32비트 | -2,147,483,648 ~ 2,147,483,647 |
u32 |
❌ | 32비트 | 0 ~ 4,294,967,295 |
i64 |
✅ | 64비트 | -2^63 ~ 2^63-1 |
u64 |
❌ | 64비트 | 0 ~ 2^64-1 |
isize |
✅ | 32/64비트 | -2^31 ~ 2^31-1 (32비트) / -2^63 ~ 2^63-1 (64비트) |
usize |
❌ | 32/64비트 | 0 ~ 2^32-1 (32비트) / 0 ~ 2^64-1 (64비트) |
isize
와 usize
는 포인터를 위한 자료형인데 크기가 시스템 아키텍쳐에 따라서 32비트가 될 수도 있고 64비트가 될 수도 있습니다.
각 자료형마다 표현할 수 있는 숫자의 범위가 틀리기 때문에, 범위를 벗어나는 숫자를 할당하지 않도록 주의해야 합니다.
예를 들어, 숫자 200
을 타입 i8
인 변수에 할당하려고 하면 컴파일 오류가 발생합니다.
let a: u8 = 200;
let b: i8 = 200;
// ^^^ error: literal out of range for `i8`
변수에 명시적으로 자료형을 지정하지 않고 정수를 할당하면 기본적으로 컴파일러가 i32
로 추론합니다.
let c = 7; // 기본 정수형 `i32`
실수형 (Floating Point)
Rust에는 실수형으로는 32비트를 사용하는 f32
와 64비트를 사용하는 f64
가 있습니다.
IEEE-754 표준을 따르는 부동소수점 자료형입니다.
let e: f32 = 2.71828;
let pi = 3.1415; // 기본 실수형 `i64`
변수에 명시적으로 자료형을 지정하지 않고 실수를 할당하면 기본적으로 컴파일러가 i64
로 추론합니다.
불리언 (Boolean)
Rust에서 bool
자료형은 다른 프로그래밍 언어처럼 true
또는 false
의 두 가지 값만 가집니다.
분기나 반복을 할 때 조건문에 많이 사용되죠.
fn main() {
let is_valid: bool = true;
if is_valid {
println!("유효합니다!");
} else {
println!("유효하지 않습니다!");
}
}
문자형 (Character)
Rust에서 문자를 표현할 때는 char
자료형을 사용합니다.
char
자료형에는 4바이트 크기의 유니코드 문자 하나를 표현할 수 있습니다.
1바이트가 아니라 4바이트이기 때문에 영어 알파벳 뿐만 아니라 한국어를 포함한 다국어, 이모지를 할당할 수도 있습니다.
let english: char = 'a';
let korean: char = '뷁';
let smiley: char = '😃';
참고로 Rust에서 문자 값은 반드시 작은 따옴표로 감싸줘야 합니다. 큰 따옴표로 감싸면 한 글자라도 문자열 슬라이스로 인식됩니다.
유닛 (Unit)
()
자료형은 함수에서 반환값이 없을 때 사용하는 특수한 자료형입니다.
Kotlin을 써보셨다면 익숙한 자료형일텐데요.
굳이 JavaScript에서 비교하자면 Undefined
자료형이 그나마 가장 비슷한 개념일 것 같습니다.
fn print_rust() {
println!("Rust");
}
let result = print_rust(); // result의 타입은 ()
튜플 (Tuple)
튜플은 서로 다른 자료형의 값들을 하나로 묶을 때 사용하는 자료형입니다. 각 요소는 인덱스로 접근할 수 있으며, 구조 분해 할당(Destructuring)을 통해 여러 변수에 할당할 후에 접근할 수도 있습니다.
(자료형1, 자료형2, ...)
형식으로 자료형을 명시하여 다양한 자료형의 여러 개 담을 수 있는 변수를 선언할 수 있습니다.
fn main() {
let user: (u8, char, bool) = (30, 'M', true);
println!("나이: {}, 성별: {}, 활성화: {}", user.0, user.1, user.2);
let (age, gender, active) = user;
println!("나이: {}, 성별: {}, 활성화: {}", age, gender, active);
}
튜플 자료형은 함수에서 두 개 이상의 값을 반환하고 싶을 때도 유용하게 사용됩니다. Python이나 Kotlin을 써보신 분들께는 아주 익숙한 자료형일거라 생각합니다.
배열 (Array)
배열은 동일한 자료형의 값 여러 개를 묶을 때 사용하는 자료형입니다. 튜플과 마찬가지로 담을 수 있는 요소의 개수, 즉 배열의 길이는 고정됩니다.
[자료형; 개수]
형식으로 자료형을 명시하여 동일한 자료형의 값을 여러 개 담을 수 있는 변수를 선언할 수 있습니다.
fn main() {
let scores: [u8; 3] = [85, 90, 95];
for score in scores {
println!("점수: {score}");
}
}
[값; 개수]
형식으로 초기화하면 동일한 값이 여러 개 들어있는 배열을 쉽게 만들 수 있습니다.
fn main() {
let mut temperatures = [0; 3];
temperatures[0] = -10;
temperatures[1] = 20;
temperatures[2] = 40;
for temperature in temperatures {
println!("온도: {temperature}");
}
}
마치며
지금까지 Rust 프로그래밍의 중요한 기초인 원시 자료형에 대해서 알아보았습니다. Rust로 코딩을 하면서 상황에 따라 적절한 자료형을 선택해서 사용하는 것은 프로그램의 안정성과 성능에 매우 중요한 부분입니다.
Rust를 배우는 초반에는 다른 프로그래밍 언어에 비해서 자료형이 너무 많고 복잡해서 까다롭게 느껴질 수 있습니다. 하지만 엄격한 타입 시스템 때문에 런타임 오류를 최소화 하면서 안전한 코드를 작성할 수 있으니 적응을 하시면 그 가치를 충분히 느끼실 수 있으실 것입니다.