Take a note when learning Rust.
1. Shadowing
Declare a new variable with the same name as a previous variable. The first variable is shadowed by the second. In other word, the second variable overshadows the first. So, we will get the second variable when using the variable name, util the second variable shadowed by other variables, or the scope ends. Shadowing is different from mutating a variale by using a mutable varible through mut
. The variable is still immutable after we using shadowing. Also, shadowing allows variable type change.
1 | fn main() { |
Output:
1 | The value of x in the inner scope is: 12 |
2. Integer Type
Length | Signed | Unsigned |
---|---|---|
8-bit | i8 | u8 |
16-bit | i16 | u16 |
32-bit | i32 | u32 |
64-bit | i64 | u64 |
128-bit | i128 | u128 |
arch | isize | usize |
Signed variant can store numbers from -2^(n - 1)
to 2^(n - 1) - 1
.
Unsigned variant can store numbers from 0
to 2^n -1
.
3. Tuple Type
Tuple is a compound type.
- Tuples havee a fixed length
- Create a tuple by writing a comma-separated list of values inside parentheses.
- Use pattern matching to destructure a tuple value
- Access a tuple element directly by using a period
.
1 | fn main() { |
Output:
1 | Values of x, y, z are: 500, 6.4, 1 |
4. Array type
1 | fn main() { |
Output:
1 | Array a is: [1, 2, 3, 4, 5] |
5. Interesting expression usage
We can assian an expression to a variable, since expression can evaluates to a value.
1 | fn main() { |
Output:
1 | The value of y is: 4 |
6. Loop
Here are a couple of loop methods interesting.
- Loop can return a valueOutput:
1
2
3
4
5
6
7
8
9
10
11
12
13fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("The result is {result}");
}1
The result is 20
- Loop labels to disambiguate between multiple loopsOutput:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21fn main() {
let mut count = 0;
'counting_up: loop {
println!("count = {count}");
let mut remaining = 10;
loop {
println!("remaining = {remaining}");
if remaining == 9 {
break;
}
if count == 2 {
break 'counting_up;
}
remaining -= 1;
}
count += 1;
}
println!("End count = {count}");
}1
2
3
4
5
6
7
8
9count = 0
remaining = 10
remaining = 9
count = 1
remaining = 10
remaining = 9
count = 2
remaining = 10
End count = 2
7. Ownership rules
- Each value in Rust has an owner.
- There can only be one owner at a time.
- When the owner goes out of scope, the value will be dropped.
8. Move
1 | fn main() { |
The reason is that types such as integers that have a known size at compile time are stored entirely on the stack
, so copies of the actual values are quick to make.
Rust has a special annotation called the Copy
trait that we can place on types that are stored on the stack
. Here are some of the types that implement Copy
:
- All the integer types, such as u32.
- The Boolean type, bool, with values true and false.
- All the floating point types, such as f64.
- The character type, char.
- Tuples, if they only contain types that also implement Copy. For example, (i32, i32) implements Copy, but (i32, String) does not.
8. References and borrowing
- At any given time, you can have either one mutable reference or any number of immutable references.
- References must always be valid.
Examples
- Can not borrow as mutable more than once at a time
1
2
3
4
5
6
7
8fn main() {
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s; // BIG PROBLEM
println!("{}, {}", r1, r2);
} - Can not have a mutable reference while we have an immutable one to the same value
1
2
3
4
5
6
7
8
9fn main() {
let mut s = String::from("hello");
let r1 = &s; // no problem
let r2 = &s; // no problem
let r3 = &mut s; // BIG PROBLEM
println!("{}, {}, and {}", r1, r2, r3);
}1
2
3
4
5
6
7
8
9
10
11fn main() {
let mut s = String::from("hello");
let r1 = &s; // no problem
let r2 = &s; // no problem
println!("{} and {}", r1, r2);
// variables r1 and r2 will not be used after this point
let r3 = &mut s; // no problem
println!("{}", r3);
}
9. Dangling references
1 | fn main() { |