You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In real life applications, most of the times we have to pass variable bindings to other functions or assign them to other variable bindings. In this case, we are **referencing** the original binding; **borrow** the data of it.
6
+
> 💭 When we discussed [Ownership](/docs/ownership/#ownership)we mentioned that, _"each piece of data has a single owner, and data is only scoped to its owner (💯 if it is not borrowed)."_
> To receive something with the promise of returning it.
10
+
In a general sense, borrowing is taking something with the promise of returning it. In Rust, borrowing is the act of creating a reference (via `&` or `&mut`) to a value without copying the data or taking ownership of it. This is quite similar to the concept of "passing by reference" in other languages.
12
11
13
-
## Shared & Mutable borrowings
14
-
15
-
⭐️ There are two types of Borrowing,
12
+
```rust
13
+
fnmain() {
14
+
leta=3;
15
+
letb=&a; // 💡 Referencing
16
+
letc=*b; // 💡 Dereferencing
16
17
17
-
1.**Shared Borrowing**`(&T)`
18
+
println!("{a} {b} {c}"); // 3 3 3
19
+
}
18
20
19
-
* A piece of data can be **borrowed by a single or multiple users**, but **data should not be altered**.
21
+
// `a` has the ownership of 3. `b` is a reference to the data of `a`.
22
+
// `c` is a separate variable/ separate ownership, created by copying the data of `a`.
23
+
// ⭐️ `let c = *b;` is similar to `let c = a;`, we just use dereferenced reference to get the data.
24
+
// 💯 `let c = *b;` is only possible when `a` is a Copy type. If `a` is not Copy type, we will get a compiler error.
25
+
```
20
26
21
-
2.**Mutable Borrowing**`(&mut T)`
27
+
In Rust, a reference cannot outlive the value it borrows. This kind of reference is called a "dangling reference" which occurs when a program tries to access memory that has already been deallocated.
22
28
23
-
* A piece of data can be **borrowed and altered by a single user**, but the data should not be accessible for any other users at that time.
29
+
```rust
30
+
fnmain() {
31
+
leta:&f64;
24
32
25
-
## Rules for borrowings
33
+
{
34
+
letb=3.14159; // 💡b owns the data and b is scoped inside the { } block
35
+
a=&b;
36
+
} // 💡b is dropped here along with its allocated data; Rust doesn't allow us to refer to data after it is dropped
26
37
27
-
There are very important rules regarding borrowing,
38
+
println!("{a}"); // ❌ Compile-time error: "b does not live long enough"
39
+
}
40
+
```
28
41
29
-
1. One piece of data can be borrowed **either** as a shared borrow **or** as a mutable borrow **at a given time. But not both at the same time**.
42
+
The Rust Borrow Checker identifies these errors at compile time.
30
43
31
-
2. Borrowing **applies for both copy types and move types**.
44
+
```rust
45
+
5|letb=3.14159;
46
+
|--binding `b` declaredhere
47
+
6|a=&b;
48
+
|^^borrowedvaluedoesnotlivelongenough
49
+
7| }
50
+
|- `b` droppedherewhilestillborrowed
51
+
8|
52
+
9|println!("{a}");
53
+
|-borrowlaterusedhere
54
+
```
32
55
33
-
3. The concept of **Liveness** ↴
56
+
References are pointers that are either a thin pointer that points to a Sized type or a fat pointer which points to an Unsized type.
34
57
35
58
```rust
36
-
fnmain() {
37
-
letmuta=vec![1, 2, 3];
38
-
letb=&muta; // &mut borrow of `a` starts here
39
-
// ⁝
40
-
// some code // ⁝
41
-
// some code // ⁝
42
-
} // &mut borrow of `a` ends here
43
-
59
+
usestd::mem::size_of_val;
44
60
45
61
fnmain() {
46
-
letmuta=vec![1, 2, 3];
47
-
letb=&muta; // &mut borrow of `a` starts here
48
-
// some code
49
-
50
-
println!("{:?}", a); // trying to access `a` as a shared borrow, so giving an error
51
-
} // &mut borrow of `a` ends here
62
+
leta=3; // 💡 3 store on the stack
63
+
letb: [i32; 3] = [1, 2, 3]; // 💡 [1, 2, 3] store inline on the stack
52
64
65
+
letc=&a; // 💡 c is a thin pointer (&i32) - [ptr]
66
+
letd=&b; // 💡 d is also a thin pointer (&[i32; 3]) - [ptr]
67
+
lete=&b[1..]; // 💡 [2, 3]; e is a fat pointer (&[i32]) - [ptr, len]
53
68
54
-
fnmain() {
55
-
letmuta=vec![1, 2, 3];
56
-
{
57
-
letb=&muta; // &mut borrow of `a` starts here
58
-
// any other code
59
-
} // &mut borrow of `a` ends here
69
+
println!("\nSize of actual data: a: {} bytes, b: {} bytes",
70
+
size_of_val(&a), size_of_val(&b) // 4 and 12
71
+
);
60
72
61
-
println!("{:?}", a); // allow borrowing `a` as a shared borrow
73
+
println!("Size of the references: c: {} bytes, d: {} bytes, e: {} bytes",
💡 Let’s see how to use shared and mutable borrowings in examples.
79
+
> 💡 An array of Copy types stores its data inline (store elements contiguously) wherever it is declared. It lives on the stack as a local variable, in the binary if it is `static`, or on the heap if it is wrapped in a `Box` or `Vec`.
80
+
>
81
+
> ```rust
82
+
> STACK
83
+
> [ 1, 2, 3 ] // let b: [i32; 3] = [1, 2, 3];
84
+
>
85
+
> [ ptr ] // let d = &b; 💡 pointer points to 0th index element of the array + len(3) is baked into the Type
86
+
> (ThinPtr)
87
+
>
88
+
> [ ptr, len ] // let e = &b[1..]; 💡 pointer points to 1st index element of the array
A borrow doesn't necessarily last until the end of the curly braces `{ }`. Instead, it lasts only until its last use.
149
+
150
+
- The `&mut` operator creates a new mutable reference to the data. When passing a `&mut` to a function, the reference is scoped to that function call.
151
+
152
+
```rust
153
+
fnmain() {
154
+
letmuta=String::from("AAA");
155
+
156
+
println!("{a}");
157
+
mutate_and_print(&muta); // 💡 A mutable reference; AAA-BBB
158
+
mutate_and_print(&muta); // 💡 A mutable reference; AAA-BBB-BBB
159
+
160
+
letb=&muta; // 💡 A mutable reference; AAA-BBB-BBB-CCC while printing
161
+
b.push_str("-CCC");
162
+
println!("{a}");
163
+
164
+
mutate_and_print(&muta); // 💡 A mutable reference; AAA-BBB-BBB-CCC-BBB
165
+
}
166
+
167
+
fnmutate_and_print(a:&mutString) {
168
+
a.push_str("-BBB");
169
+
println!("{a}");
170
+
}
171
+
172
+
// Eventhough `let b = &mut a;` creates a new mutable reference, its last use only till the next line. So, the sahred reference creation via `println!()` is still valid and trigger no error.
173
+
```
174
+
175
+
- Shared references are Copy types while mutable references are Move types. However, when we pass a mutable reference to a function or assign it to a new variable with an explicit type, Rust doesn't move the original reference. Instead, Rust creates a new temporary reference to the same data with a shorter lifetime. This is called "reborrowing" as it's a "borrow from a borrow". While this new reborrow is active, the original reference cannot be used.
176
+
177
+
```rust
178
+
fnmain() {
179
+
letmuta=128;
180
+
letb=&muta; // 💡 First mutable reference
181
+
182
+
{
183
+
letc:&muti32=b; // 💡 Reborrowing; A temporary mutable reference
184
+
*c+=1;
185
+
186
+
// println!("{b}"); // cannot borrow `b` as immutable because it is also borrowed as mutable for `c`
0 commit comments