hyunsu blog

홈강좌Rust 입문·중급 47편 중 5편째

Rust 정수 타입 — i32 가 기본이고, 담을 수 있는 범위가 정해져 있다

목차 · 4
  1. 01정수 타입은 이름에 규칙이 있다
  2. 02타입마다 담을 수 있는 범위가 다르다
  3. 03범위를 넘는 숫자는 컴파일러가 막는다
  4. 04실습 — 정수끼리 나누면 몫만 남는다

Rust 정수는 크기와 부호에 따라 i8~i128, u8~u128 로 나뉘고, 타입을 적지 않으면 i32 가 된다. 타입마다 담을 수 있는 범위가 정해져 있다. 범위를 넘는 숫자를 쓰면 컴파일러가 미리 막고, 실행 중에 넘치면 디버그 빌드에서는 프로그램이 멈춘다.

01 정수 타입은 이름에 규칙이 있다

이름의 첫 글자와 숫자만 보면 된다.

  • i 는 음수도 담는 정수, u 는 0 이상만 담는 정수다.
  • 뒤의 숫자는 크기(비트 수)다. 8·16·32·64·128 이 있다.
타입크기쓰는 때
i3232비트따로 정할 이유가 없을 때. 타입을 안 적으면 이것이다
u88비트0~255 로 충분한 작은 값
i64 · u6464비트아주 큰 값
usize컴퓨터마다 다름배열의 길이·번호 (9편)

변수 이름 뒤에 : 타입 을 쓰면 타입을 직접 정한다. let age: u8 = 30; 처럼 쓴다.

02 타입마다 담을 수 있는 범위가 다르다

i8::MIN 은 "i8 의 가장 작은 값" 이다. :: 는 "그 타입에 딸린 것" 이라는 표시다. 네 타입의 범위를 찍어 보자. println! 의 {} 는 뒤에 쉼표로 적은 값이 차례로 들어가는 자리다.

rust
fn main() {
    println!("i8:  {} ~ {}", i8::MIN, i8::MAX);
    println!("u8:  {} ~ {}", u8::MIN, u8::MAX);
    println!("i32: {} ~ {}", i32::MIN, i32::MAX);
    println!("u32: {} ~ {}", u32::MIN, u32::MAX);
}
text
   Compiling ints v0.1.0 (~/projects/ints)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.16s
     Running `target/debug/ints`
i8:  -128 ~ 127
u8:  0 ~ 255
i32: -2147483648 ~ 2147483647
u32: 0 ~ 4294967295

u8 은 0~255, i32 는 약 -21억~21억이다. 비트 수가 같으면 i 와 u 가 담는 개수는 같다. i 는 그 절반을 음수에 쓸 뿐이다.

03 범위를 넘는 숫자는 컴파일러가 막는다

나이를 u8 로 정해 놓고 300 을 넣어 보자.

rust
fn main() {
    let age: u8 = 300;
    println!("{age}");
}
text
error: literal out of range for `u8`
 --> src/main.rs:2:19
  |
2 |     let age: u8 = 300;
  |                   ^^^
  |
  = note: the literal `300` does not fit into the type `u8` whose range is `0..=255`
  = note: `#[deny(overflowing_literals)]` on by default
  1. 무엇 — 숫자(literal)가 u8 의 범위를 벗어났다.
  2. 어디 — 2번째 줄의 300.
  3. 고치는 법 — note: 가 이유를 적어 준다. 300 은 0..=255(0부터 255까지)에 들어가지 않는다. 값을 줄이거나 u16 처럼 더 큰 타입을 쓴다.

이번에는 255 에 1 을 더한다. 계산 결과가 넘친다.

rust
fn main() {
    let level: u8 = 255;
    let next = level + 1;
    println!("{next}");
}
text
error: this arithmetic operation will overflow
 --> src/main.rs:3:16
  |
3 |     let next = level + 1;
  |                ^^^^^^^^^ attempt to compute `u8::MAX + 1_u8`, which would overflow
  |
  = note: `#[deny(arithmetic_overflow)]` on by default

컴파일러가 계산을 미리 해 보고 u8::MAX + 1, 즉 256 이 된다고 알려 준다. 두 오류 모두 error[E…] 같은 번호가 없다. 대신 #[deny(…)] 표시가 있다. 이 검사는 기본으로 오류(deny)로 정해져 있다는 뜻이다. 경고(warn)로 정해진 검사도 있는데, 3편의 안 쓰는 mut 가 #[warn(unused_mut)] 였다.

값이 실행 중에 정해지면(사용자가 입력한 숫자 같은 것) 컴파일러는 미리 알 수 없다. 그때 넘치면 cargo run 으로 만든 디버그 빌드는 그 자리에서 프로그램을 멈춘다(패닉). 조용히 틀린 값으로 넘어가지 않는다.

04 실습 — 정수끼리 나누면 몫만 남는다

피자 7조각을 두 사람이 나눈다.

rust
fn main() {
    let total = 7;
    let people = 2;
    println!("{}", total / people);
    println!("{}", total % people);
}
text
   Compiling ints v0.1.0 (~/projects/ints)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.16s
     Running `target/debug/ints`
3
1

정수끼리 나누면(/) 소수점 아래를 버리고 몫 3 만 남는다. % 는 나머지다. 7 을 2 로 나누면 나머지가 1 이다. 3.5 가 필요하면 실수를 써야 한다. 다음 편에서 실수와, 정수를 실수로 바꾸는 as 를 다룬다.