Data Types
Rust’s type system is one of the major differences from JavaScript. Let’s explore the various data types in Rust and how they compare to JavaScript.
Type Systems: Static vs Dynamic
Section titled “Type Systems: Static vs Dynamic”JavaScript is dynamically typed, meaning variables can change types at runtime:
let value = 42; // Number
value = "hello"; // Now it's a String
value = true; // Now it's a Boolean
Rust is statically typed, meaning every variable’s type must be known at compile time:
let value = 42; // i32 by default
// value = "hello"; // ❌ Error: mismatched types
// value = true; // ❌ Error: mismatched types
In Rust, you can explicitly annotate types:
let age: u32 = 30;
let name: String = String::from("Alice");
let is_active: bool = true;
Scalar Types
Section titled “Scalar Types”Integers
Section titled “Integers”JavaScript has a single numeric type (Number
) for integers and floating-point values, plus BigInt
for large integers.
Rust has multiple integer types based on size and signedness:
Type | Size (bits) | Range | JS Equivalent |
---|---|---|---|
i8 | 8 | -128 to 127 | Number |
u8 | 8 | 0 to 255 | Number |
i16 | 16 | -32,768 to 32,767 | Number |
u16 | 16 | 0 to 65,535 | Number |
i32 | 32 | -2,147,483,648 to 2,147,483,647 | Number |
u32 | 32 | 0 to 4,294,967,295 | Number |
i64 | 64 | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 | BigInt |
u64 | 64 | 0 to 18,446,744,073,709,551,615 | BigInt |
i128 | 128 | Very large range | BigInt |
u128 | 128 | Very large range | BigInt |
isize | arch | Depends on architecture | Number |
usize | arch | Depends on architecture | Number |
let a: i32 = 42; // 32-bit signed integer
let b: u64 = 1000000; // 64-bit unsigned integer
let c = 98_222; // Underscores for readability, like 98,222
let d = 0xff; // Hex value
let e = 0o77; // Octal value
let f = 0b1111_0000; // Binary value
In JavaScript, all these would be:
const a = 42; // Number
const b = 1000000; // Number
const c = 98222; // Number
const d = 0xff; // Hex value (255)
const e = 0o77; // Octal value (63)
const f = 0b11110000; // Binary value (240)
const g = 9007199254740992n; // BigInt for numbers > 2^53
Floating-Point Numbers
Section titled “Floating-Point Numbers”Rust has two floating-point types:
Type | Size (bits) | Precision | JS Equivalent |
---|---|---|---|
f32 | 32 | Single precision | Number |
f64 | 64 | Double precision | Number |
let x: f64 = 2.0; // Double-precision float (default)
let y: f32 = 3.0; // Single-precision float
In JavaScript:
const x = 2.0; // Number (always double precision)
Booleans
Section titled “Booleans”Both languages have boolean types:
let t: bool = true;
let f: bool = false;
const t = true;
const f = false;
Characters
Section titled “Characters”JavaScript doesn’t have a dedicated character type; single characters are just strings of length 1.
Rust’s char
type represents a Unicode scalar value:
let c: char = 'z';
let z: char = 'ℤ';
let heart: char = '❤';
In JavaScript:
const c = 'z'; // String of length 1
const z = 'ℤ'; // String of length 1 (but 2 bytes in UTF-16)
const heart = '❤'; // String of length 1 (but 2 bytes in UTF-16)
Compound Types
Section titled “Compound Types”Tuples
Section titled “Tuples”Rust has tuples, which are fixed-length collections of values of different types:
let tup: (i32, f64, u8) = (500, 6.4, 1);
// Destructuring
let (x, y, z) = tup;
println!("y is: {}", y); // 6.4
// Accessing by index
let five_hundred = tup.0; // 500
JavaScript added tuple-like behavior with array destructuring:
// JavaScript doesn't have real tuples, but we can use arrays
const tup = [500, 6.4, 1];
// Destructuring
const [x, y, z] = tup;
console.log(y); // 6.4
// Accessing by index
const fiveHundred = tup[0]; // 500
Arrays
Section titled “Arrays”Rust arrays have a fixed length and contain elements of the same type:
// Array with 5 elements of type i32
let a: [i32; 5] = [1, 2, 3, 4, 5];
// Initialize an array with the same value repeated
let b = [3; 5]; // Equivalent to [3, 3, 3, 3, 3]
// Access elements
let first = a[0]; // 1
JavaScript arrays are dynamic and can contain elements of different types:
// JavaScript arrays are dynamic
const a = [1, 2, 3, 4, 5];
a.push(6); // Can add elements
// Can contain mixed types
const mixed = [1, "two", true, { four: 4 }];
// Access elements the same way
const first = a[0]; // 1
For dynamic arrays in Rust, you’d use a Vec
(vector), which we’ll cover in the Collections section.
Strings
Section titled “Strings”Strings are a complex topic in Rust, especially coming from JavaScript.
JavaScript Strings
Section titled “JavaScript Strings”In JavaScript, strings are simple:
const greeting = "Hello, world!";
const name = 'Alice';
const template = `Hello, ${name}!`;
// String methods
const upperName = name.toUpperCase();
const sub = greeting.substring(0, 5); // "Hello"
Rust Strings
Section titled “Rust Strings”Rust has two string types: String
and &str
:
// String literals are &str (string slices)
let greeting: &str = "Hello, world!";
// String type is growable, heap-allocated
let mut name = String::from("Alice");
name.push_str(" Smith"); // Now "Alice Smith"
// Creating a String from a &str
let s = "initial".to_string();
let s = String::from("initial");
// Concatenation
let hello = String::from("Hello, ");
let world = String::from("world!");
let hello_world = hello + &world; // Note: hello is moved here
// Format macro (like template literals)
let name = "Alice";
let greeting = format!("Hello, {}!", name);
The main differences:
&str
is immutable and often used for string literals or views into stringsString
is growable, heap-allocated, and owned- Rust strings are UTF-8 encoded, which affects how they’re indexed and manipulated
Type Conversion
Section titled “Type Conversion”JavaScript Type Coercion
Section titled “JavaScript Type Coercion”JavaScript often performs implicit type conversion:
console.log("5" + 1); // "51" (string)
console.log("5" - 1); // 4 (number)
console.log(5 + true); // 6 (number)
Rust Explicit Conversion
Section titled “Rust Explicit Conversion”Rust requires explicit conversion:
let x = 5;
// let y = x + "10"; // ❌ Error: cannot add `&str` to `{integer}`
// Explicit conversion
let y = x + 10; // 15
let s = x.to_string() + "10"; // "510"
let z = x + "10".parse::<i32>().unwrap(); // 15
Special Types
Section titled “Special Types”The Unit Type
Section titled “The Unit Type”Rust has a unit type ()
, which represents the absence of a value:
// Functions with no return value implicitly return the unit type
fn do_something() {
println!("Hello");
// Implicitly returns ()
}
JavaScript doesn’t have an equivalent; functions that don’t return anything implicitly return undefined
.
The Option Type
Section titled “The Option Type”Instead of null
or undefined
, Rust uses Option<T>
to represent a value that might be absent:
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;
// Using Option requires handling both cases
match some_number {
Some(n) => println!("Got a number: {}", n),
None => println!("No number"),
}
JavaScript equivalent:
const someNumber = 5;
const someString = "a string";
const absentNumber = null;
// Check for null/undefined
if (someNumber !== null && someNumber !== undefined) {
console.log(`Got a number: ${someNumber}`);
} else {
console.log("No number");
}
Type Inference
Section titled “Type Inference”Both languages can infer types, but Rust’s inference is more sophisticated and happens at compile time:
// Rust infers these types
let x = 5; // i32
let y = 3.0; // f64
let active = true; // bool
// JavaScript also has type inference, but at runtime
let x = 5; // number
let y = 3.0; // number
let active = true; // boolean
Type Aliases
Section titled “Type Aliases”Rust allows you to create type aliases:
type Kilometers = i32;
let distance: Kilometers = 5;
In JavaScript with TypeScript:
type Kilometers = number;
let distance: Kilometers = 5;
Next Steps
Section titled “Next Steps”Now that you understand Rust’s data types, you’re ready to move on to Functions, where we’ll explore how to define and use functions in Rust compared to JavaScript.