Что такое Ownership
Ownership — ключевая концепция Rust, гарантирующая безопасность памяти без сборщика мусора. Каждое значение имеет одного владельца.
▸Правила ownership
У каждого значения есть только один владелец
Когда владелец выходит из области видимости, значение уничтожается
Можно передать владение (move)
▸Move semantics
1fn main() {2 let s1 = String::from("hello");3 let s2 = s1; // s1 перемещено в s245 // println!("{}", s1); // Ошибка: s1 больше не владеет данными6 println!("{}", s2); // OK7}
▸Клонирование
1fn main() {2 let s1 = String::from("hello");3 let s2 = s1.clone(); // Глубокое копирование45 println!("s1 = {}, s2 = {}", s1, s2); // Оба работают6}
Borrowing
▸Immutable borrowing
1fn main() {2 let s = String::from("hello");34 // Можно иметь несколько иммутабельных заимствований5 let r1 = &s;6 let r2 = &s;7 println!("{} and {}", r1, r2);8}
▸Mutable borrowing
1fn main() {2 let mut s = String::from("hello");34 // Можно иметь только одно мутабельное заимствование5 let r1 = &mut s;6 r1.push_str(", world");7 println!("{}", r1);8}
▸Правила borrowing
1fn main() {2 let mut s = String::from("hello");34 let r1 = &s; // OK5 let r2 = &s; // OK6 // let r3 = &mut s; // Ошибка: нельзя мутабельно заимствовать при иммутабельных78 println!("{} and {}", r1, r2);910 let r3 = &mut s; // OK после последнего использования r1 и r211 r3.push_str("!");12}
Lifetimes
▸Базовый синтаксис
1// Без lifetime'ов2fn first_word(s: &str) -> &str {3 let bytes = s.as_bytes();4 for (i, &item) in bytes.iter().enumerate() {5 if item == b' ' {6 return &s[0..i];7 }8 }9 &s[..]10}1112// С lifetime'ами (явно)13fn first_word<'a>(s: &'a str) -> &'a str {14 // ...15}
▸Lifetime'ы в структурах
1struct ImportantExcerpt<'a> {2 part: &'a str,3}45impl<'a> ImportantExcerpt<'a> {6 fn level(&self) -> i32 {7 38 }910 fn announce_and_return_part(&self, announcement: &str) -> &str {11 println!("Attention please: {}", announcement);12 self.part13 }14}
▸Lifetime elision
1// Это:2fn first_word(s: &str) -> &str { ... }34// Эквивалентно:5fn first_word<'a>(s: &'a str) -> &'a str { ... }67// Правила elision:8// 1. Каждый параметр-заимствование получает свой lifetime9// 2. Если ровно один входной lifetime, он присваивается всем выходным10// 3. Если есть &self или &mut self, lifetime self присваивается всем выходным
String vs &str
▸String
1// Heap-allocated, owns its data2let s = String::from("hello");3let s = "hello".to_string();4let s = format!("hello {}", "world");
▸&str
1// Borrowed string slice2let s: &str = "hello"; // Строковый литерал3let s: &str = &string[..]; // Срез строки
Ownership в функциях
▸Передача ownership
1fn takes_ownership(s: String) {2 println!("{}", s);3} // s уничтожается45fn makes_copy(x: i32) {6 println!("{}", x);7} // x копируется89fn main() {10 let s = String::from("hello");11 takes_ownership(s); // ownership перемещается1213 let x = 5;14 makes_copy(x); // x копируется, оригинальное значение доступно15 println!("{}", x); // OK16}
▸Возврат ownership
1fn gives_ownership() -> String {2 let s = String::from("hello");3 s // ownership возвращается вызывающему4}56fn takes_and_gives_back(s: String) -> String {7 s // ownership возвращается8}
Типичные ошибки
▸Use after move
1let s1 = String::from("hello");2let s2 = s1;3// println!("{}", s1); // Ошибка
▸Double mutable borrow
1let mut s = String::from("hello");2let r1 = &mut s;3let r2 = &mut s; // Ошибка
▸Подвисающие ссылки
1fn dangle() -> &String { // Ошибка: нет lifetime2 let s = String::from("hello");3 &s // s уничтожается, ссылка недействительна4}
Практические паттерны
▸Clone when necessary
1fn process(data: &Data) {2 // Если нужно передать владение3 let data_clone = data.clone();4 spawn(move || {5 // Используем data_clone6 });7}
▸Arc для разделяемого владения
1use std::sync::Arc;23let data = Arc::new(vec![1, 2, 3]);45for i in 0..3 {6 let data = Arc::clone(&data);7 spawn(move || {8 println!("{:?}", data);9 });10}
Заключение
Ownership и borrowing — основа Rust. Понимание этих концепций критически важно для написания безопасного и эффективного кода. На собеседовании спрашивают про правила ownership, как работает borrowing, и типичные ошибки компилятора.