RustBrock/ownership.md
2025-01-17 20:51:38 -07:00

316 lines
9.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.
- Theres 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]