타입 시스템 — 동적 타입의 자유를 포기하면 얻는 것
Ruby의 duck typing vs Rust의 정적 타입 추론
Ruby에서 x = "hello"였다가 x = 42로 바꿔도 아무 문제없다. 메서드만 있으면 되니까(duck typing). Rust에서는 이게 불가능하다.
let x = "hello"; // 컴파일러가 &str로 추론
// x = 42; // 컴파일 에러! expected &str, found integer
기본 타입들
Ruby의 Integer, Float, String 등이 Rust에서는 더 세분화된다.
| Ruby | Rust | 설명 |
|---|---|---|
Integer |
i32, i64, u32, u64 등 |
부호/크기 명시 |
Float |
f32, f64 |
32비트/64비트 |
true/false |
bool |
동일 |
String |
String, &str |
두 종류 (별도 포스트) |
nil |
없음 (Option<T>) |
별도 포스트 |
Symbol |
없음 (enum 사용) | 별도 포스트 |
Array |
Vec<T> |
제네릭 |
Hash |
HashMap<K, V> |
제네릭 |
i32는 32비트 부호 있는 정수, u64는 64비트 부호 없는 정수. Ruby에서는 이런 구분을 신경 쓸 일이 없었지만, Rust에서는 메모리 크기와 부호를 명시한다.
타입 추론
Rust는 타입을 안 써도 대부분 알아서 추론한다.
let x = 42; // i32로 추론 (기본값)
let y = 3.14; // f64로 추론 (기본값)
let name = "sehwa"; // &str로 추론
let nums = vec![1, 2, 3]; // Vec<i32>로 추론
명시가 필요한 경우도 있다. 컴파일러가 결정을 못 하는 상황이다.
// 이건 에러 — 42가 i32인지 u64인지 모름
let x = "42".parse(); // error: type annotations needed
// 이렇게 알려줘야 함
let x: i32 = "42".parse().unwrap();
// 또는
let x = "42".parse::<i32>().unwrap(); // turbofish 문법
::<i32>를 turbofish라고 부른다. 생김새가 물고기 같다고. Rust 커뮤니티 특유의 네이밍.
튜플과 배열
Ruby의 배열은 아무 타입이나 섞을 수 있다. [1, "hello", true] 가능. Rust의 Vec은 한 가지 타입만 담는다.
여러 타입을 묶고 싶으면 튜플을 쓴다.
let pair: (i32, &str) = (42, "hello");
println!("{}", pair.0); // 42
println!("{}", pair.1); // hello
Ruby에서 return a, b로 여러 값을 반환하는 패턴이 Rust에서는 튜플이다.
fn divide(a: f64, b: f64) -> (f64, f64) {
(a / b, a % b) // 몫과 나머지
}
let (quotient, remainder) = divide(10.0, 3.0);
타입 변환 — as와 From/Into
Ruby에서 42.to_s, "42".to_i로 자유롭게 변환한다. Rust에서는 두 가지 방법이 있다.
// 숫자 간 변환 — as (Ruby의 .to_i/.to_f에 가까움)
let x: i32 = 42;
let y: f64 = x as f64;
// 문자열 → 숫자 — parse (실패 가능하므로 Result 반환)
let n: i32 = "42".parse().unwrap();
// 숫자 → 문자열 — to_string
let s: String = 42.to_string();
as는 숫자 간 변환에서만 쓴다. 문자열 변환은 parse()나 to_string()을 쓴다. Ruby처럼 .to_i로 끝나는 게 아니라 Result를 반환하기 때문에, 변환 실패를 무시할 수 없다.
핵심 포인트
Rust의 기본 정수형은 i32, 부호/크기를 명시한다
타입 추론이 강력해서 대부분 명시할 필요 없다
여러 타입을 묶으려면 튜플 (i32, &str)을 사용한다
타입 변환은 as(숫자), parse()(문자열→숫자)로 명시적
장점
- ✓ 타입 에러가 컴파일 타임에 잡힌다 — Ruby의 TypeError가 사라진다
- ✓ IDE 자동완성이 완벽하게 작동한다 — Ruby보다 훨씬 정확
단점
- ✗ Ruby의 자유로운 타입 전환이 안 되니 초반에 답답하다
- ✗ i32와 i64를 더하려면 명시적 변환이 필요하다