mirror of
https://github.com/darkicewolf50/RustBrock.git
synced 2025-06-15 13:04:18 -06:00
316 lines
9.8 KiB
Markdown
316 lines
9.8 KiB
Markdown
# Ownership
|
||
|
||
## Basic Rules
|
||
1. Each value in Rust has an owner
|
||
1. There can only be one owner at a time
|
||
1. When owner goes out of scope, the value will be dropped
|
||
|
||
# Scope
|
||
Range in which a variable is valid, normally indicated by a {inside is a scope}
|
||
|
||
Variables are only valid between the time they are declared and the end of a scope
|
||
|
||
# Shallow Copy
|
||
This causes a move (shallow copy) to s2 where s2 has the value previously owned by s1
|
||
```rust
|
||
let s1 = String::from("hello");
|
||
// s1 is no longer the owner variable of the string, move has occured to s2
|
||
let s2 = s1;
|
||
// this would not work because s1 is no longer valid/has a pointer to the string
|
||
println!("{s1}");
|
||
```
|
||
|
||
|
||
## Deep Copy
|
||
This is a deep copy this only happens by default with primitive values
|
||
Rust will not create a deep copy by default, therefore copies are cheap in terms of performance
|
||
This will only copy the part on the stack, this is the same functionality as let y = x.clone()
|
||
```rust
|
||
let x = 5;
|
||
let y = x;
|
||
```
|
||
### Deep Copy Heap
|
||
If you want a deep copy for things on both the stack and heap then use .clone() method
|
||
Indication that some code executing has a high performance cost
|
||
```rust
|
||
let s1 = String::from("hello");
|
||
let s2 = s1.clone();
|
||
println!("s1 = {s1}, s2 = {s2}");
|
||
```
|
||
|
||
## Copy Trait
|
||
Will not be allowed to be implemented if Drop trait has already been implemented
|
||
trying to implement a copy trait with a drop trait will cause a complimation error
|
||
Drop is only used when it needs something special to leave scope
|
||
|
||
Any group of simple scalar values implement copy
|
||
Such as:
|
||
- Integers like u32
|
||
- Booleans with true/false values
|
||
- All Float Point Values like f32, f64
|
||
- Char
|
||
- Tuples only if they only contain values that implement copy themselves
|
||
-
|
||
|
||
## Assignment
|
||
When you assign a completely new value to something on the heap then then the drop function qill be called on the old data on the heap automaitcally
|
||
|
||
```rust
|
||
let mut s = String::from("hello");
|
||
// drop called on the old "hello"
|
||
s = String::from("ahoy");
|
||
// outputs ahoy
|
||
println!("{s}, world!");
|
||
```
|
||
|
||
# Function Ownership
|
||
This is the same as the move/shallow copy
|
||
|
||
```rust
|
||
fn main() {
|
||
let s = String::from("hello"); // s comes into scope
|
||
takes_ownership(s); // s's value moves into the function...
|
||
// ... and so is no longer valid here
|
||
// if s was called here the s variable would no longer be valid/in scope
|
||
let x = 5; // x comes into scope
|
||
makes_copy(x); // x would move into the function,
|
||
// but i32 is Copy, so it's okay to still
|
||
// use x afterward
|
||
// x is allowed to be used here because of low cost to copy **see deep copy heap
|
||
} // Here, x goes out of scope, then s. But because s's value was moved, nothing
|
||
// special happens.
|
||
fn takes_ownership(some_string: String) { // some_string comes into scope
|
||
println!("{some_string}");
|
||
} // Here, some_string goes out of scope and `drop` is called. The backing
|
||
// memory is freed.
|
||
fn makes_copy(some_integer: i32) { // some_integer comes into scope
|
||
println!("{some_integer}");
|
||
} // Here, some_integer goes out of scope. Nothing special happens.
|
||
```
|
||
|
||
|
||
# Returning Multiple Values using a tuple
|
||
|
||
```rust
|
||
fn main() {
|
||
let s1 = String::from("hello");
|
||
let (s2, len) = calculate_length(s1); // s1 gives up ownership to funct
|
||
println!("The length of '{s2}' is {len}.");
|
||
}
|
||
fn calculate_length(s: String) -> (String, usize) {
|
||
let length = s.len(); // len() returns the length of a String
|
||
(s, length) // s gives up ownership to return a value
|
||
}
|
||
```
|
||
|
||
# References
|
||
|
||
## Rules
|
||
1. At any given time, you can have either one mutable reference or any number of immutable references.
|
||
1. References must always be valid
|
||
|
||
Giving access to a variable without transfering ownership/moving it
|
||
|
||
Can have unlimited read-only/immutable references
|
||
|
||
this is created by
|
||
```rust
|
||
let var_name: &var_to_borrow_value
|
||
```
|
||
|
||
## Mutable References
|
||
Can only have 1 mutable reference
|
||
|
||
this is created by
|
||
```rust
|
||
let var_name: &mut var_to_borrow_value
|
||
```
|
||
|
||
This prevents three bad behavoirs
|
||
- Two or more pointers access the same data at the same time.
|
||
- At least one of the pointers is being used to write to the data.
|
||
- There’s no mechanism being used to synchronize access to the data.
|
||
|
||
Also allowed
|
||
```rust
|
||
{
|
||
let r1 = &mut s;
|
||
} // r1 goes out of scope here, so we can make a new reference with no
|
||
problems.
|
||
let r2 = &mut s;
|
||
```
|
||
|
||
## Mixing References
|
||
NOT ALLOWED IN ONE SCOPE
|
||
ONLY 1 Mutable Reference PER SCOPE
|
||
OR UNLIMITED IMMUTABLE PER SCOPE
|
||
|
||
REFERNECE SCOPE ENDS WHEN IT IS LAST USED
|
||
|
||
BAD CODE EXAMPLE
|
||
```rust
|
||
let r1 = &s; // no problem
|
||
let r2 = &s; // no problem
|
||
let r3 = &mut s; // BIG PROBLEM
|
||
```
|
||
|
||
OK code, not great, only possible becasue r1 and r2 are out of scope when r3 is declared
|
||
```rust
|
||
let mut s = String::from("hello");
|
||
let r1 = &s; // no problem
|
||
let r2 = &s; // no problem
|
||
println!("{r1} and {r2}");
|
||
// variables r1 and r2 will not be used after this point
|
||
let r3 = &mut s; // no problem
|
||
println!("{r3}");
|
||
```
|
||
|
||
## Dangling Reference
|
||
Compiler will guarantee that this cannot happen
|
||
This happens when a pointer refers to an address in memory that was freed by another variable and possibly used by something else
|
||
|
||
```rust
|
||
fn dangle() -> &String { // dangle returns a reference to a String
|
||
let s = String::from("hello"); // s is a new String
|
||
&s // we return a reference to the String, s
|
||
} // Here, s goes out of scope, and is dropped. Its memory goes away.
|
||
// Danger!
|
||
```
|
||
|
||
Fixed Code
|
||
|
||
```rust
|
||
fn dangle() -> String { // dangle returns a String
|
||
let s = String::from("hello"); // s is a new String
|
||
|
||
s
|
||
} // Here, s goes out of scope, and ownership is transfered and nothing other than the s namespcae is deallocated
|
||
```
|
||
|
||
|
||
## Slices
|
||
Let you reference a continuous sequence of elements
|
||
The sequence does not have an owner is reference only
|
||
it is considered a slice which is a kind of reference
|
||
|
||
example use
|
||
want to find the first word in a string words are seperated by a space
|
||
|
||
This has a problem the usize only makes sense for the reference to thee string as it currently is, if the value the string was references changes than usize makes no sense/is not connected to the state of the string/out of sync
|
||
```rust
|
||
// dont want ownership so reference is fine, returns usize this is the index of the space
|
||
fn first_word(s: &String) -> usize {
|
||
// convert string to an array of bytes so that we can go through it
|
||
let bytes = s.as_bytes();
|
||
// create iterator for bytes, method that returns each element in an array
|
||
// enumerate, returns it as a part of a tuple instead which is destructed
|
||
// i is the index and titem is the iteam itself to compair in the if statement
|
||
// need to be a reference to that ownership doesnt change
|
||
for (i, &item) in bytes.iter().enumerate() {
|
||
if item == b' ' {
|
||
// index of the space
|
||
return i;
|
||
}
|
||
}
|
||
// if there are no spaces in the string then return the whole length
|
||
s.len()
|
||
}
|
||
```
|
||
|
||
### String Slices
|
||
Slices must occur at the utf-8 character boundaries, slices at the character within a charcter's boundaries will cause an error
|
||
This is constructed by
|
||
```rust
|
||
let var_name = &string_to_refer[start_index..end_index]
|
||
```
|
||
|
||
.. is range syntx in rust
|
||
|
||
```rust
|
||
let a = [..2]; // range starts at 0 and goes to 2
|
||
let b = [0..2]; // the same as a in terms of range
|
||
|
||
let c = [3..len]; // goes from 3 to the end
|
||
let d = [3..]; // the same as c in terms of range
|
||
|
||
let f = &s[..]; // the whole range of s, where s is some string
|
||
```
|
||
|
||
|
||
Example
|
||
```rust
|
||
let s = String::from("hello world");
|
||
let hello = &s[0..5]; // hello
|
||
let world = &s[6..11]; // world
|
||
```
|
||
|
||
Revised Example with String Slice
|
||
The value returned by this function is guaranteed to be related to the vairable passed in
|
||
If it is not valid the complier will catch it and throw out a borrowing rule error (two reference types mixing)
|
||
```rust
|
||
fn first_word(s: &String) -> &str {
|
||
let bytes = s.as_bytes();
|
||
for (i, &item) in bytes.iter().enumerate() {
|
||
if item == b' ' {
|
||
return &s[0..i];
|
||
}
|
||
}
|
||
&s[..]
|
||
}
|
||
```
|
||
|
||
### String Literal Slices
|
||
```rust
|
||
let s: &str = "hello World"
|
||
```
|
||
|
||
This is the same type as a string slice bbut it is a special kind that points to a specific part of the binary
|
||
|
||
### String Slices as a parameter
|
||
to improve the function you can change the reference form &String to &str to also handle string literals without sacrifices to the base functionality
|
||
this takes advantage of deref coercions
|
||
```rust
|
||
fn first_word(s: &str) -> &str
|
||
```
|
||
|
||
|
||
### Example Perfected
|
||
|
||
```rust
|
||
fn main() {
|
||
let my_string = String::from("hello world");
|
||
// `first_word` works on slices of `String`s, whether partial or whole
|
||
let word = first_word(&my_string[0..6]);
|
||
let word = first_word(&my_string[..]);
|
||
// `first_word` also works on references to `String`s, which are equivalent
|
||
// to whole slices of `String`s
|
||
let word = first_word(&my_string);
|
||
let my_string_literal = "hello world";
|
||
// `first_word` works on slices of string literals, whether partial or whole
|
||
let word = first_word(&my_string_literal[0..6]);
|
||
let word = first_word(&my_string_literal[..]);
|
||
// Because string literals *are* string slices already,
|
||
// this works too, without the slice syntax!
|
||
let word = first_word(my_string_literal);
|
||
}
|
||
|
||
fn first_word(s: &str) -> &str {
|
||
let bytes = s.as_bytes();
|
||
for (i, &item) in bytes.iter().enumerate() {
|
||
if item == b' ' {
|
||
return &s[0..i];
|
||
}
|
||
}
|
||
&s[..]
|
||
}
|
||
```
|
||
|
||
### Other Slices
|
||
This is a more general use of a slice
|
||
```rust
|
||
let a = [1, 2, 3, 4, 5];
|
||
let slice = &a[1..3];
|
||
assert_eq!(slice, &[2, 3]);
|
||
```
|
||
|
||
This has a slice type of &[i32] |