Professional Documents
Culture Documents
## What is Ownership
Some langs
- have garbage collection that constantly looks for no-longer used memory as the
program runs (Python)
- programmer must explicitly allocate and free the memory (C++)
- in Rust memory is managed through a system of ownership with a set of rules that
the compiler checks. If any of the rules are violated, the program won’t compile.
This is managed by borrow checker. It’s like it auto-inserts memory allocations and
frees for you, giving you the convenience of a garbage collector with the speed and
efficiency of manual management.
- `Stack`
- stores values in order it gets them and removes in opposite order (LIFO)
- data stored in stack must have a known fixed size
- pushing value onto stack is not called allocation. Pushing is faster than
allocating because no need to search for a spot. Accessing on the stack is faster
too.
- `Heap`
- can store data with unknown at compile time or changing size
- less organized - memory allocator finds a spot in memory that's big enough
for the value. Marks the memory as busy and returns a pointer - this is
`allocation`
- the pointer to the heap has a fixed size, so it can be stored in a stack. But
for the actual data we go to the heap following the pointer.
> When your code calls a function, the values passed into the function (including,
potentially, pointers to data on the heap) and the function’s local variables get
pushed onto the stack. When the function is over, those values get popped off the
stack.
The relationship between scopes and when variables are valid is similar to that in
other programming languages
The known size types (trivial) are stored on stack. So they can be easily copied to
a new scope for use and pushed/popped.
- instead of `shallow copy` Rust uses `move` meaning only the last variable
(pointer on stack) is valid an points to the memory on the heap. This is designed
to avoid double free error when we are trying to free the same memory twice.
This copies only the pointer and invalidates the previous pointer.
- by default `let s2 = s1` does the faster more, not a deep copy. For `deep copy`
use `let s2 = s1.clone();`.
This copies the value on the heap too and creates a new pointer on stack to the
newly allocated heap memory
Passing variable into a func will `move` or `copy`. One var was moved into func it
becomes invalid after the function call.
```rust
fn main() {
let s = String::from("hello"); // s comes into scope
} // Here, x goes out of scope, then s. But because s's value was moved, nothing
// special happens.
- `&` - reference
- creating a reference = `borrowing`
- you can't modify a borrowed var by default, use `&mut` instead.
- to prevent `data race`:
- At any given time, you can have either one mutable reference or any
number of immutable references.
- can have only one mutable reference to a data at a time
- can't have mut and immut refs at a time
- multiple immut refs are allowed
- reference is valid until it was last used. That means we can create a
contradicting ref if the previous ref is already dropped:
```rust
let mut s = String::from("hello");
`dangling pointer` - a reference to memory that was already freed or maybe even
given to someone else. Rust guarantees there will be no dangling pointers.
Won't work:
```rust
fn dangle() -> &String { // dangle returns a reference to a String
Works:
```rust
fn no_dangle() -> String {
let s = String::from("hello");