• Что бы вступить в ряды "Принятый кодер" Вам нужно:
    Написать 10 полезных сообщений или тем и Получить 10 симпатий.
    Для того кто не хочет терять время,может пожертвовать средства для поддержки сервеса, и вступить в ряды VIP на месяц, дополнительная информация в лс.

  • Пользаватели которые будут спамить, уходят в бан без предупреждения. Спам сообщения определяется администрацией и модератором.

  • Гость, Что бы Вы хотели увидеть на нашем Форуме? Изложить свои идеи и пожелания по улучшению форума Вы можете поделиться с нами здесь. ----> Перейдите сюда
  • Все пользователи не прошедшие проверку электронной почты будут заблокированы. Все вопросы с разблокировкой обращайтесь по адресу электронной почте : info@guardianelinks.com . Не пришло сообщение о проверке или о сбросе также сообщите нам.

Rust: Ownership, Borrowing (References), Lifetimes, Move/Copy/Clone semantics

Lomanu4 Оффлайн

Lomanu4

Команда форума
Администратор
Регистрация
1 Мар 2015
Сообщения
1,481
Баллы
155
? Why Care About These Concepts?


  • Reliability
    These concepts allow the Rust compiler to prevent common resource errors such as:
    • Memory leaks
    • Dangling pointers
    • Double frees
    • Accessing uninitialized memory

  • Convenience

    The Rust compiler can automatically free resources using these concepts — no need for manual free or a garbage collector.


  • Performance
    Resource management without a garbage collector enables:
    • Faster performance
    • Suitability for real-time systems
?️ How to Use These Features?

  • These features are enforced by the compiler through compile-time checks.
  • You don't usually do something manually — instead, you follow Rust's rules.
  • Understanding these concepts is essential to fix compiler error messages during development.
? Basic Memory Management Terminology


  • A variable is a name for a memory location holding a value of some type.


  • Memory can be allocated in three regions:
    • Stack: Automatically allocated when a function is called, and freed when the function returns.
    • Heap: Requires explicit allocation and deallocation (handled by Rust’s ownership model).
    • Static memory: Lives for the entire duration of the program.
? Common Memory Errors


  • Dangling Pointer:
    A pointer to memory that has been freed or was never initialized.


  • Memory Leak:
    Memory allocated on the heap is never freed — e.g., forgetting to release memory.


  • Uninitialized Memory:
    Using memory before it has been properly allocated or assigned a value.


  • Double Free:
    Attempting to free the same memory more than once — either on the same variable or on a copy.
⚠ What Can Go Wrong on the Stack?


  • Since memory is automatically allocated and freed, there are no memory leaks, uninitialized memory, or double free problems.


  • The function might return a pointer to a value on the stack, leading to a dangling pointer.


  • Rust prevents this by simply checking that no such references are returned — see stack-dangling-pointer.rs.
❌ Example: Returning a reference to a local variable


fn create_ref() -> &i32 {
let number = 10;
&number // ❌ This will cause a compile error
}
Compiler Error:


error[E0515]: cannot return reference to local variable `number`
  • Don’t return references to local function variables — copy or move the value out of the function.
✅ Correct version: Move the value out


fn create_value() -> i32 {
let number = 10;
number // ✅ Move the value instead of returning a reference
}
? What Can Go Wrong on the Heap?


  • A reference might be used after the memory was reallocated or freed, leading to a dangling pointer.


  • The borrow checker prevents the reallocation by not allowing a mutable and other reference at the same time.
    An immutable reference is needed to push on a vector and possibly reallocate it — see heap-reallocation-dangling-pointer.rs.
❌ Example: Reallocation with active immutable borrow


fn main() {
let mut vec = vec![1, 2, 3];
let first = &vec[0]; // Immutable borrow
vec.push(4); // ❌ May cause reallocation
println!("{}", first); // Use after potential reallocation
}
Compiler Error:


error[E0502]: cannot borrow `vec` as mutable because it is also borrowed as immutable
  • Don’t do it, and if you really need a mutable reference paired with other references, use the std::cell module.
✅ Correct version: Borrow after mutation


fn main() {
let mut vec = vec![1, 2, 3];
vec.push(4); // ✅ Mutate first
let first = &vec[0]; // Borrow afterwards
println!("{}", first);
}
  • Rust prevents the use after free case by making sure no reference is used after its lifetime has ended, i.e. the value was dropped — see heap-dropped-dangling-pointer.rs.
❌ Example: Reference lives longer than the value


fn main() {
let r;
{
let vec = vec![1, 2, 3];
r = &vec[0]; // ❌ `vec` goes out of scope here
}
println!("{}", r); // Use after drop
}
Compiler Error:


error[E0597]: `vec` does not live long enough
  • Don’t do it, or if you really run into this problem, you might need shared ownership with the std::rc module.
  • The borrow checker also does not allow a value to be moved to another variable that could reallocate or free the memory while there are references — see heap-move-dangling-pointer.rs.
❌ Example: Move after borrow


fn main() {
let vec = vec![1, 2, 3];
let r = &vec;
let moved = vec; // ❌ Moving vec while r still exists
println!("{:?}", r);
}
Compiler Error:


error[E0505]: cannot move out of `vec` because it is borrowed
  • Don’t move a value to another variable and then use a reference to it you created before.
✅ Correct version: Use after move or avoid borrowing before move


fn main() {
let vec = vec![1, 2, 3];
let moved = vec; // ✅ Move without borrowing first
println!("{:?}", moved);
}
? What Is Ownership?

  • Rust automatically frees memory, so there are no memory leaks or double free calls.
  • Rust does this without a garbage collector.
? How Does Rust Manage Memory?

  • The concept is simple: Rust calls a destructor (drop) whenever the lifetime of a value ends, i.e., when the value goes out of scope ({} block ends).

fn main() {
{
let _s = String::from("hello");
// `_s` is dropped here automatically
}
// memory is freed
}
❌ The Problem with Shallow Copies

  • Some values, like a Vec, contain heap-allocated data.
  • A shallow copy (only copying pointer and metadata) would cause a double free error if both copies tried to free the same memory.
✅ Rust prevents this with Move Semantics

  • When you assign one variable to another, Rust moves the value instead of copying it (unless it implements Copy).
? Move Example (move-semantics.rs)


fn main() {
let vec = vec![1, 2, 3];
let moved = vec; // vec is moved
// println!("{:?}", vec); // ❌ error: use of moved value
}
? Compiler Error:


error[E0382]: borrow of moved value: `vec`
  • Don’t use a value after it was moved.
  • If you really need a deep copy, use .clone():

fn main() {
let vec = vec![1, 2, 3];
let cloned = vec.clone(); // deep copy
println!("{:?}", vec); // ✅ ok to use
}
⚠ .clone() is often unnecessary and can lead to performance issues if overused.
? Copy vs Move


  • Primitive types (e.g., i32, bool, char) are copied by default.
    • No heap data → no double free risk.

fn main() {
let a = 42;
let b = a; // a is copied, not moved
println!("a = {}, b = {}", a, b); // ✅ both are usable
}

  • Types with heap data (e.g., Vec, String) are moved by default.


  • You can make your own types Copy-able by implementing the Copy trait:
Example: Copy trait (copy-semantics.rs)


#[derive(Copy, Clone)]
struct Point {
x: i32,
y: i32,
}

fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = p1; // p1 is copied, not moved
println!("p1: {}, {}", p1.x, p1.y); // ✅ still valid
}
  • If a type is not Copy, Rust will give an error like:

move occurs because `config` has type `Config`, which does not implement the `Copy` trait
✅ Don’t implement Copy if you can live with using references.

✅ You can also just implement Clone and call .clone() explicitly — see clone-semantics.rs.
? Summary and Miscellaneous Info


  • Ownership, borrowing, and lifetimes enable the Rust compiler to:
    • Detect and prevent memory errors
    • Handle memory automatically and safely

  • Safe Rust guarantees memory safety — no undefined behavior from memory misuse.


  • You can use unsafe Rust inside an unsafe {} block if needed.


  • Rust understands how to free memory even in:
    • Loops
    • if/match clauses
    • Iterators
    • Partial moves from structs

  • ✅ If your program compiles, you get these memory safety guarantees without a runtime garbage collector.


Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

 
Вверх Снизу