Определение: Владение — это ключевая концепция 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 только там, где это действительно необходимо, и документируйте свои действия.