Skip to content

Commit 1f0f787

Browse files
ownership
1 parent 5a24182 commit 1f0f787

1 file changed

Lines changed: 266 additions & 0 deletions

File tree

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
---
2+
title: Ownership, Borrowing, and Slices in Rust
3+
---
4+
5+
In this lesson, we’ll take a closer look at three of Rust’s most important concepts: **ownership**, **borrowing**, and **slices**.
6+
These are the foundations of Rust’s **memory safety**, allowing it to manage memory **without a garbage collector** and to **prevent data races at compile time**.
7+
8+
9+
### 1. Ownership
10+
11+
Ownership is **Rust’s memory management system**. Instead of a garbage collector, Rust tracks **who owns each piece of data** and **frees it automatically** when no longer needed.
12+
13+
Key ideas:
14+
- No garbage collector.
15+
- Strict rules, checked during compilation.
16+
- Violating them causes compilation errors.
17+
18+
**Stack** and **Heap** memory:
19+
20+
- **Stack**:
21+
- Stores data in **Last-In, First-Out (LIFO)** order.
22+
- Extremely fast because the memory location is always at the “top of the stack.”
23+
- Requires values to have a **known, fixed size** at compile time.
24+
- Automatically freed when the variable goes out of scope.
25+
26+
- **Heap**:
27+
- Stores **dynamically sized or growable data** (like `String` or `Vec`).
28+
- Memory must be **requested at runtime** from the allocator.
29+
- Access is **slower** because you must **follow a pointer** from the stack to the heap.
30+
- Memory must eventually be freed, which is where **ownership rules** come in.
31+
32+
Rust’s **ownership system** exists to **safely manage heap memory**, automatically cleaning up resources and preventing data races.
33+
34+
### **The 3 Ownership Rules**
35+
36+
1. **Each value in Rust has a single owner** (a variable that “owns” the value).
37+
2. **When the owner goes out of scope, the value is dropped** (memory freed).
38+
3. **Ownership can be moved, but not copied by default** (unless the type is `Copy` or you explicitly `clone`).
39+
40+
41+
#### **Example:**
42+
43+
```rust
44+
fn main() {
45+
let s1 = String::from("hello"); // s1 owns the string
46+
let s2 = s1; // ownership moves to s2
47+
48+
// println!("{}", s1); // ERROR: s1 no longer owns the value
49+
println!("{}", s2); // Works
50+
}
51+
```
52+
53+
- After `s2 = s1`, **s1 is invalidated** to prevent **double free** errors.
54+
- When `main` ends, `s2` is dropped, and Rust frees the memory automatically.
55+
56+
---
57+
58+
### **Copy vs Clone**
59+
60+
Rust **treats data differently** depending on **where it lives**:
61+
62+
- **Stack-only data (simple types)****Copied automatically**
63+
- **Heap-allocated data (complex types)****Moved by default**
64+
65+
66+
#### **1. Copy Types (Stack-Only)**
67+
68+
- Examples: integers (`i32`), booleans (`bool`), characters (`char`), and tuples of `Copy` types.
69+
- These types are **small and fixed-size**, so Rust **copies them cheaply** instead of moving them.
70+
71+
```rust
72+
fn main() {
73+
let x = 5; // i32 is a Copy type
74+
let y = x; // A new copy of 5 is created on the stack
75+
76+
println!("x = {}, y = {}", x, y); // Both valid
77+
}
78+
```
79+
80+
Stack values are **duplicated instantly**, so `x` still owns its 5 and `y` has its own 5.
81+
82+
83+
#### **2. Move Semantics (Heap Data)**
84+
85+
- Types like `String`, `Vec<T>`, or any custom type **holding heap memory** are **moved by default**.
86+
- Assigning them **transfers ownership** instead of copying the underlying heap memory (which could be expensive).
87+
88+
```rust
89+
fn main() {
90+
let s1 = String::from("hello"); // s1 owns the heap data
91+
let s2 = s1; // s1 is MOVED into s2
92+
93+
// println!("{}", s1); // ERROR: s1 is no longer valid
94+
println!("{}", s2); // Only s2 can be used now
95+
}
96+
```
97+
98+
**Why move instead of copy?**
99+
- Copying large heap data automatically could be **slow**.
100+
- Move avoids extra work while still keeping memory safe.
101+
102+
103+
#### **3. Clone (Deep Copy)**
104+
105+
- If you **want a real copy of the heap data**, call `.clone()`.
106+
107+
```rust
108+
fn main() {
109+
let s1 = String::from("hello");
110+
let s2 = s1.clone(); // Copies heap data as well
111+
112+
println!("s1 = {}, s2 = {}", s1, s2); // Both valid
113+
}
114+
```
115+
116+
- **Move**: only the pointer and metadata are copied; old owner is invalid. (Cheap)
117+
- **Clone**: heap data is copied too; both owners are valid. (More expensive)
118+
119+
120+
Think of **ownership like house keys**:
121+
122+
- **Copy** - Making a **duplicate key** for a small box (cheap and simple).
123+
- **Move** - Handing your **only key** to someone else (you can’t access it anymore).
124+
- **Clone** - **Building a whole new house** with its own key (expensive).
125+
126+
---
127+
128+
### 2. Borrowing and References
129+
130+
If we want to **use a value in multiple places** without transferring ownership.
131+
Rust solves this with **borrowing**, which allows **references** to a value.
132+
133+
- A **reference** is like a **pointer** that guarantees memory safety.
134+
- Borrowing allows **access without taking ownership**, so the original variable stays valid.
135+
- **No runtime overhead**: the compiler ensures safety rules.
136+
137+
138+
### **Immutable References (`&T`)**
139+
140+
An immutable reference lets you **read data without taking ownership**:
141+
142+
```rust
143+
fn main() {
144+
let s = String::from("hello");
145+
146+
let len = calculate_length(&s); // Borrow immutably
147+
println!("The length of '{}' is {}.", s, len); // s is still valid
148+
}
149+
150+
fn calculate_length(s: &String) -> usize {
151+
s.len() // Can read, cannot modify
152+
}
153+
```
154+
155+
- `&s` is a **reference** (borrow).
156+
- The original variable **keeps ownership**.
157+
- You **cannot modify** through an immutable reference.
158+
159+
160+
### **Mutable References (`&mut T`)**
161+
162+
If we want to **modify** a value without transferring ownership, we use **mutable references**:
163+
164+
```rust
165+
fn main() {
166+
let mut s = String::from("hello");
167+
168+
change(&mut s); // Borrow mutably
169+
println!("{}", s); // Output: hello, world
170+
}
171+
172+
fn change(some_string: &mut String) {
173+
some_string.push_str(", world");
174+
}
175+
```
176+
177+
- Only **one mutable reference** is allowed at a time.
178+
- This prevents **data races**, ensuring **safe concurrent access**.
179+
180+
181+
### **Borrowing Rules**
182+
183+
1. **You can have either:**
184+
- Any number of **immutable references**
185+
- **OR** one **mutable reference**
186+
2. **References must always be valid** (no dangling pointers).
187+
188+
These rules ensure Rust can **guarantee memory safety** at compile time.
189+
190+
---
191+
192+
## 3. Slices
193+
194+
A **slice** is a **reference to part of a collection**.
195+
Slices let you **work with sub-sections of data without copying**.
196+
197+
198+
### **String Slices (`&str`)**
199+
200+
```rust
201+
fn main() {
202+
let s = String::from("hello world");
203+
204+
let hello = &s[0..5]; // Slice of "hello"
205+
let world = &s[6..11]; // Slice of "world"
206+
207+
println!("{} {}", hello, world);
208+
}
209+
```
210+
211+
- `&s[start..end]` creates a slice from `start` (inclusive) to `end` (exclusive).
212+
- `&s[..]` creates a slice of the **entire string**.
213+
214+
---
215+
216+
### **Slices in Functions**
217+
218+
Slices are commonly used to **avoid copying data** when processing collections:
219+
220+
```rust
221+
fn first_word(s: &str) -> &str { // Accepts &String or string literal
222+
let bytes = s.as_bytes();
223+
224+
for (i, &item) in bytes.iter().enumerate() {
225+
if item == b' ' {
226+
return &s[..i]; // Slice until first space
227+
}
228+
}
229+
230+
&s[..] // If no space, return entire string
231+
}
232+
233+
fn main() {
234+
let s = String::from("hello world");
235+
let word = first_word(&s);
236+
println!("First word: {}", word);
237+
}
238+
```
239+
240+
- `&str` is already a **string slice**.
241+
- Returning a slice is **efficient** and avoids extra allocations.
242+
243+
---
244+
245+
### **Array Slices**
246+
247+
Slices also work with arrays:
248+
249+
```rust
250+
fn main() {
251+
let arr = [1, 2, 3, 4, 5];
252+
let slice = &arr[1..4]; // Elements 2, 3, 4
253+
254+
for val in slice {
255+
println!("{}", val);
256+
}
257+
}
258+
```
259+
260+
- Array slices are `&[T]`.
261+
- They **borrow part of the array** without copying it.
262+
263+
---
264+
265+
### Exercises
266+
// to be added later

0 commit comments

Comments
 (0)