Определение: Владение — это ключевая концепция Rust, которая управляет тем, как память выделяется и освобождается. Каждое значение в Rust имеет "владельца" (owner), и когда владелец выходит из области видимости, память автоматически освобождается.
fn main() { let s = String::from("hello"); // s становится владельцем строки // Здесь s владеет памятью } // s выходит из области видимости, память освобождается
Нюансы: Владение предотвращает ошибки вроде двойного освобождения памяти или использования указателей после освобождения. Передача владения (move) происходит при присваивании или передаче в функцию, если тип не реализует трейт Copy
.
Примечание: Типы вроде i32
или bool
реализуют Copy
, поэтому они копируются, а не перемещаются.
Определение: Заимствование позволяет временно использовать значение без передачи владения. В Rust есть два типа заимствования: изменяемое (&mut
) и неизменяемое (&
).
fn main() { let mut s = String::from("hello"); let r1 = &s; // Неизменяемое заимствование let r2 = &mut s; // Изменяемое заимствование r2.push_str(", world"); println!("{}", r1); // Ошибка: r1 не может использоваться после &mut }
Нюансы: В Rust действует правило: либо одно изменяемое заимствование, либо любое количество неизменяемых, но не оба одновременно. Это предотвращает гонки данных (data races).
Предупреждение: Нарушение правил заимствования приведёт к ошибке компиляции. Всегда проверяйте области видимости ссылок.
Определение: Время жизни — это аннотация ('a
), указывающая, как долго ссылка остаётся действительной. Оно необходимо для обеспечения безопасности ссылок в функциях и структурах.
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } fn main() { let s1 = String::from("long"); let s2 = String::from("short"); let result = longest(&s1, &s2); println!("Longest: {}", result); }
Нюансы: Компилятор Rust автоматически выводит времена жизни в большинстве случаев, но явные аннотации нужны, когда связи между входными и выходными ссылками неоднозначны.
Определение: Трейт — это способ определения общего поведения для типов. Похож на интерфейсы в других языках, но с возможностью реализации по умолчанию.
trait Printable { fn print(&self) { println!("Default print"); } } struct Item { name: String, } impl Printable for Item { fn print(&self) { println!("Item: {}", self.name); } } fn main() { let item = Item { name: String::from("Book") }; item.print(); // Выведет: Item: Book }
Нюансы: Трейты используются для обобщённого программирования (generics) и динамической диспетчеризации через dyn Trait
.
Определение: Крейт — это единица компиляции в Rust, которая может быть библиотекой или исполняемым файлом. Корневой файл крейта — это обычно main.rs
(для бинарного крейта) или lib.rs
(для библиотеки).
// main.rs mod my_module { pub fn hello() { println!("Hello from module!"); } } fn main() { my_module::hello(); }
Нюансы: Крейты управляются через Cargo
, инструмент сборки Rust. Внешние крейты подключаются через Cargo.toml
.
Определение: Сопоставление с образцом — это мощный механизм для работы с данными через конструкции вроде match
и if let
.
fn main() {
let number = Some(7);
match number {
Some(n) if n > 0 => println!("Positive: {}", n),
Some(n) => println!("Non-positive: {}", n),
None => println!("None"),
}
}
Нюансы: match
требует исчерпывающего покрытия всех вариантов, что делает код надёжным.
Определение: Блок unsafe
позволяет обойти некоторые проверки безопасности Rust, например, для работы с сырыми указателями или вызова C-кода.
fn main() {
let mut num = 5;
let ptr = &mut num as *mut i32;
unsafe {
*ptr = 10; // Изменение через сырой указатель
}
println!("{}", num); // Выведет: 10
}
Нюансы: Использование unsafe
требует осторожности, так как оно может привести к неопределённому поведению (UB).
Предупреждение: Используйте unsafe
только там, где это действительно необходимо, и документируйте свои действия.