|
| 1 | +--- |
| 2 | +title: Variables and data types |
| 3 | +--- |
| 4 | + |
| 5 | +Now let's dive into how Rust handles variables and data. This is where you'll see some key differences from languages like JavaScript or Python. |
| 6 | + |
| 7 | +#### **Immutability by Default:** |
| 8 | +This is one of Rust's core principles. By default, variables in Rust are **immutable**, meaning once you give them a value, you cannot change that value. This helps prevent unexpected bugs. |
| 9 | + |
| 10 | + * **Declaring an Immutable Variable:** |
| 11 | + ```rust |
| 12 | + fn main() { |
| 13 | + let x = 5; // 'x' is immutable. Its value is 5, and it cannot be changed. |
| 14 | + println!("The value of x is: {}", x); |
| 15 | + // x = 6; // This would cause a compile-time error! Try uncommenting it. |
| 16 | + // println!("The value of x is: {}", x); |
| 17 | + } |
| 18 | + ``` |
| 19 | + |
| 20 | + * **Making a Variable Mutable:** |
| 21 | + If you *do* want to change a variable's value, you must explicitly mark it as `mut` (short for mutable). |
| 22 | + ```rust |
| 23 | + fn main() { |
| 24 | + let mut y = 10; // 'y' is mutable. We can change its value. |
| 25 | + println!("The initial value of y is: {}", y); |
| 26 | + y = 15; // This is allowed because 'y' is mutable. |
| 27 | + println!("The new value of y is: {}", y); |
| 28 | + } |
| 29 | + ``` |
| 30 | +#### **Type Inference vs. Explicit Types:** |
| 31 | +Rust is a **statically typed** language, meaning it knows the type of every variable at compile time. However, it's also very smart and can often **infer** the type based on the value you assign. You don't always *have* to write the type. |
| 32 | + * **Type Inference (Common):** |
| 33 | + ```rust |
| 34 | + fn main() { |
| 35 | + let age = 30; // Rust infers 'age' is an integer (i32 by default) |
| 36 | + let pi = 3.14; // Rust infers 'pi' is a floating-point number (f64 by default) |
| 37 | + let is_active = true; // Rust infers 'is_active' is a boolean |
| 38 | + let initial = 'A'; // Rust infers 'initial' is a character (single quotes) |
| 39 | + let greeting = "Hello"; // Rust infers 'greeting' is a string slice (&str) |
| 40 | + println!("Age: {}, Pi: {}, Active: {}, Initial: {}, Greeting: {}", age, pi, is_active, initial, greeting); |
| 41 | + } |
| 42 | + ``` |
| 43 | + * **Explicit Type Annotation (When needed or for clarity):** |
| 44 | + You can explicitly tell Rust the type of a variable. This is useful when inference is ambiguous or for better readability. |
| 45 | + ```rust |
| 46 | + fn main() { |
| 47 | + let count: i64 = 100_000_000_000; // Explicitly a 64-bit integer |
| 48 | + let temperature: f32 = 25.5; // Explicitly a 32-bit float |
| 49 | + let message: &str = "Welcome!"; // Explicitly a string slice |
| 50 | + println!("Count: {}, Temp: {}, Message: {}", count, temperature, message); |
| 51 | + } |
| 52 | + ``` |
| 53 | +#### **Common Primitive Data Types:** |
| 54 | +Rust has several built-in primitive types: |
| 55 | + * **Integers:** `i8`, `i16`, `i32` (default), `i64`, `i128` (signed integers) and `u8`, `u16`, `u32`, `u64`, `u128` (unsigned integers). The number indicates the bits they use. `isize` and `usize` depend on the architecture (e.g., 32-bit or 64-bit). |
| 56 | + * **Floating-Point Numbers:** `f32` (single-precision), `f64` (double-precision, default). |
| 57 | + * **Booleans:** `bool` (`true` or `false`). |
| 58 | + * **Characters:** `char` (single Unicode scalar value, uses single quotes, e.g., `'A'`, `'😊'`). |
| 59 | + * **Strings:** We'll learn more about strings later, but for now, know that `&str` (string slice, immutable reference to text) and `String` (growable, owned string) are the main types. |
| 60 | + |
| 61 | +#### **Constants:** |
| 62 | + |
| 63 | +Constants are always immutable and must have their type explicitly annotated. They can be declared in any scope, including global. |
| 64 | + |
| 65 | +```rust |
| 66 | +const MAX_POINTS: u32 = 100_000; // Constants are typically named in SCREAMING_SNAKE_CASE |
| 67 | +const APP_VERSION: &str = "1.0.0"; |
| 68 | +fn main() { |
| 69 | + println!("Max points: {}", MAX_POINTS); |
| 70 | + println!("App version: {}", APP_VERSION); |
| 71 | +} |
| 72 | +``` |
| 73 | + |
| 74 | +#### **Shadowing:** |
| 75 | + |
| 76 | +Rust allows you to declare a *new* variable with the same name as a previous variable. This "shadows" the previous variable, meaning the new variable takes precedence. This is different from `mut`, as you're creating a new variable, not changing an existing one. |
| 77 | +```rust |
| 78 | +fn main() { |
| 79 | + let spaces = " "; // First 'spaces' variable (string slice) |
| 80 | + println!("Spaces (initial): '{}'", spaces); |
| 81 | + let spaces = spaces.len(); // 'spaces' is now a new variable, holding the length (an integer) |
| 82 | + println!("Spaces (length): {}", spaces); // The old 'spaces' is no longer accessible |
| 83 | +} |
| 84 | +``` |
| 85 | +Shadowing is useful when you want to transform a variable's value but keep the same name, without needing to make the original variable mutable. |
0 commit comments