콘텐츠로 이동

Rust 타입 변환

Rust는 한 타입을 다른 타입으로 알아서 바꿔 주지 않아요. 같은 식에서 i32와 f64를 섞어 쓰면 컴파일되지 않아요. 직접, 명시적으로 변환해야 해요. 이건 일부러 그렇게 만든 거예요. 다른 언어에서는 눈에 띄지 않는 자동 변환이 버그의 흔한 원인이거든요.

as 키워드를 쓰면 한 숫자 타입을 다른 숫자 타입으로 변환(캐스팅)할 수 있어요.

fn main() {
let x: i32 = 10;
let y: f64 = x as f64; // i32 -> f64
println!("{y}");
}
▶ 직접 실행해 보기

이건 생각보다 중요해요. 변환 없이 정수 두 개를 나누면, 아마 원했을 소수 결과 대신 정수 나눗셈 결과가 나와요.

fn main() {
let a = 10;
let b = 3;
println!("{}", a / b); // 3 (정수 나눗셈)
println!("{}", a as f64 / b as f64); // 3.3333333333333335
}
▶ 직접 실행해 보기

작은 타입으로 변환하면 데이터를 잃을 수 있어요

섹션 제목: “작은 타입으로 변환하면 데이터를 잃을 수 있어요”

큰 타입에서 작은 타입으로(예: i32에서 u8로) 변환해도 실패하지 않아요. 대신 넘치는 부분이 그냥 잘려 나가서, 깜짝 놀랄 만한 결과가 나올 수 있어요.

fn main() {
let big: i32 = 300;
let small = big as u8; // u8은 0~255만 담을 수 있어요
println!("{small}"); // 300이 아니라 44예요. 한 바퀴 돌아 버렸어요
}
▶ 직접 실행해 보기

as는 정수 말고도 몇 가지 타입에 더 쓸 수 있어요. 각각 알아 두면 좋은 규칙이 있어요.

fn main() {
println!("{}", 3.99_f64 as i32); // 3: 소수점 아래는 잘려요
println!("{}", -3.99_f64 as i32); // -3: 내림이 아니라 0 쪽으로 잘려요
println!("{}", 3.99_f64.round() as i32); // 4: 반올림하고 싶으면 먼저 round
println!("{}", 300.0_f64 as u8); // 255: 너무 커서 최댓값에서 멈춰요
println!("{}", -5.0_f64 as u8); // 0: 너무 작아서 최솟값에서 멈춰요
println!("{}", 'A' as u32); // 65: char의 코드 번호
println!("{}", 97_u8 as char); // a: 그리고 다시 문자로
println!("{}", true as i32); // 1 (false라면 0)
}

실행 결과:

3
-3
4
255
0
65
a
1
▶ 직접 실행해 보기

기억해 둘 점은 이래요.

  • 실수를 정수로 바꾸면 소수점 아래를 전부 버려요. 반올림하지 않아요. 가장 가까운 정수가 필요하면 먼저 .round()를 호출하세요.
  • 범위를 벗어난 실수는 들어갈 수 있는 가장 가까운 값으로 고정돼요(u8에서 300.0은 255가 돼요). 정수는 한 바퀴 돌아 버리는 것과 달라요(위에서 300은 44가 됐죠).
  • 문자는 뒤에 숫자(유니코드 코드 포인트)를 하나씩 가지고 있어요. 'A' as u32는 65예요. char로 바로 되돌릴 수 있는 건 u8뿐이에요. 더 큰 숫자는 모든 숫자가 올바른 문자인 건 아니기 때문에 char::from_u32를 써야 해요.
  • 불리언은 1 또는 0이 돼요. 반대 방향은 안 돼요. 1 as bool은 컴파일되지 않아요. 대신 n != 0 같은 비교를 쓰세요.

as로 하는 변환은 숫자 타입끼리만 돼요. 사용자가 입력한 값처럼 텍스트를 숫자로 바꾸려면 .parse()를 쓰세요.

fn main() {
let text = "42";
let number: i32 = text.parse().expect("올바른 숫자가 아니에요");
println!("{}", number + 8);
}

실행 결과:

50
▶ 직접 실행해 보기

.parse()는 실패할 수 있어요(텍스트가 아예 숫자가 아니라면요?). 그래서 숫자를 바로 돌려주는 대신 Result를 돌려줘요. Result는 아직 배우지 않았으니, 지금은 .expect("메시지")를 “실패하면 이 메시지와 함께 프로그램을 멈춰”라는 간단한 방법으로 생각하면 돼요. 제대로 처리하는 방법은 Result 페이지에서 볼 수 있어요.

반대 방향은 더 간단해요. 거의 모든 타입은 .to_string()으로 String이 될 수 있어요.

fn main() {
let number = 42;
let text = number.to_string();
println!("{}", text + "!");
}
▶ 직접 실행해 보기

시험 점수 세 개 7, 8, 10을 i32 값으로 저장하세요. 합계를 출력하고, 그다음 평균을 소수점 둘째 자리까지 출력하세요. 힌트: 정수 두 개를 나누면 8.33이 아니라 8이 나와요. 그러니 먼저 합계를 f64로 변환하세요. {:.2}는 소수점 둘째 자리까지 출력해요.

정답 보기
fn main() {
let a: i32 = 7;
let b: i32 = 8;
let c: i32 = 10;
let sum = a + b + c;
let average = sum as f64 / 3.0; // 25 / 3이 아니라 25.0 / 3.0
println!("합계: {sum}");
println!("평균: {:.2}", average);
}

실행 결과:

합계: 25
평균: 8.33
▶ 직접 실행해 보기

어떤 가게가 가격을 텍스트 "4.50"으로, 수량을 i32 값 3으로 줬어요. 텍스트를 f64로 바꾸고 수량을 곱해서 합계: 13.5를 출력하세요. 힌트: 이 페이지에서 배운 도구 두 가지가 모두 필요해요. 텍스트에는 .parse(), 수량에는 as f64예요.

정답 보기
fn main() {
let price_text = "4.50";
let quantity: i32 = 3;
let price: f64 = price_text.parse().expect("올바른 가격이 아니에요");
let total = price * quantity as f64; // f64 * f64
println!("합계: {total}");
}

실행 결과:

합계: 13.5
▶ 직접 실행해 보기

as f64가 없으면 Rust는 f64와 i32의 곱셈을 거부해요. 숫자 타입을 절대 알아서 섞어 주지 않기 때문이에요.

  • Rust는 타입을 절대 자동으로 변환하지 않아요. 항상 명시적으로 변환해요.
  • 숫자 타입 사이의 변환에는 as를 써요: x as f64, x as u8.
  • 더 작은 타입으로 변환하면 조용히 잘려요. 에러가 나지 않으니 조심하세요.
  • 실수를 정수로 바꾸면 소수점 아래가 잘려요(반올림하려면 먼저 .round()). char와 bool도 숫자로 변환할 수 있어요.
  • 텍스트를 숫자로 바꿀 때는 .parse(), 반대 방향은 .to_string()을 써요.

이해도 점검

1. Rust에서 변환 없이 10 / 3처럼 정수 두 개를 나누면 어떻게 되나요?

2. 300_i32 as u8을 하면 어떻게 되나요?

3. 문자열을 숫자로 바꾸는 변환은 실패할 수 있어요. 그래서 .parse()는 무엇을 돌려주나요?

점수: 0 / 3