Type System β What You Gain by Giving Up Dynamic Typing
Ruby duck typing vs Rust static type inference
In Ruby, x = "hello" can become x = 42 with zero complaints. Methods are all that matter (duck typing). In Rust, this is impossible.
let x = "hello"; // compiler infers &str
// x = 42; // compile error! expected &str, found integer
Basic types
Ruby's Integer, Float, String split into finer types in Rust.
| Ruby | Rust | Note |
|---|---|---|
Integer |
i32, i64, u32, u64 etc. |
sign/size explicit |
Float |
f32, f64 |
32-bit/64-bit |
true/false |
bool |
same |
String |
String, &str |
two kinds (separate post) |
nil |
none (Option<T>) |
separate post |
Symbol |
none (use enum) | separate post |
Array |
Vec<T> |
generic |
Hash |
HashMap<K, V> |
generic |
i32 is a 32-bit signed integer, u64 is a 64-bit unsigned integer. In Ruby you never think about this. In Rust, memory size and sign are explicit.
Type inference
Rust infers types in most cases without annotation.
let x = 42; // inferred as i32 (default)
let y = 3.14; // inferred as f64 (default)
let name = "sehwa"; // inferred as &str
let nums = vec![1, 2, 3]; // inferred as Vec<i32>
Sometimes annotation is needed β when the compiler can't decide.
// error β is 42 i32 or u64?
let x = "42".parse(); // error: type annotations needed
// tell it explicitly
let x: i32 = "42".parse().unwrap();
// or
let x = "42".parse::<i32>().unwrap(); // turbofish syntax
::<i32> is called turbofish. Because it looks like a fish. Rust community naming at its finest.
Tuples and arrays
Ruby arrays can mix types. [1, "hello", true] is fine. Rust's Vec holds one type only.
For multiple types, use tuples.
let pair: (i32, &str) = (42, "hello");
println!("{}", pair.0); // 42
println!("{}", pair.1); // hello
Ruby's return a, b pattern maps to tuples in Rust.
fn divide(a: f64, b: f64) -> (f64, f64) {
(a / b, a % b) // quotient and remainder
}
let (quotient, remainder) = divide(10.0, 3.0);
Type conversion β as and From/Into
In Ruby, 42.to_s and "42".to_i convert freely. Rust has two approaches.
// numeric conversion β as (like .to_i/.to_f)
let x: i32 = 42;
let y: f64 = x as f64;
// string β number β parse (returns Result since it can fail)
let n: i32 = "42".parse().unwrap();
// number β string β to_string
let s: String = 42.to_string();
as is for numeric conversions only. String conversions use parse() or to_string(). Unlike Ruby's .to_i, parse() returns a Result β you can't ignore conversion failure.
Key Points
Default integer is i32, sign/size are explicit
Type inference is powerful β rarely need annotations
Use tuples (i32, &str) to bundle multiple types
Type conversion is explicit: as (numeric), parse() (stringβnumber)
Pros
- ✓ Type errors caught at compile time β no more Ruby TypeError
- ✓ IDE autocomplete works perfectly β far more accurate than Ruby
Cons
- ✗ Missing Ruby's free type switching feels frustrating at first
- ✗ Adding i32 and i64 requires explicit conversion