fn main() {
println!("Hello, world!");
}//— однострочный комментарий/* */— многострочный комментарий
let x = 5; // неизменяемая переменная
let mut y = 10; // изменяемая переменная
const PI: f64 = 3.14; // константа
let a: i32 = 42;
let b: f64 = 3.14;
let c: bool = true;
let d: char = '🦀';
let s: &str = "строка";if x > 0 {
println!("Положительное");
} else {
println!("Ноль или отрицательное");
}
for i in 0..5 {
println!("{}", i);
}
while x < 10 {
x += 1;
}
loop {
break;
}let dir = Direction::North;
match dir {
Direction::North => println!("Север"),
Direction::South => println!("Юг"),
_ => println!("Другое направление"),
}let mut v = vec![1, 2, 3];
v.push(4);
let s = String::from("hello");
let slice = &s[0..2];
let tup = (1, "hi", true);
let (x, y, z) = tup;fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 {
Err("Деление на ноль".to_string())
} else {
Ok(a / b)
}
}
let result = divide(4.0, 2.0);
match result {
Ok(val) => println!("Результат: {}", val),
Err(e) => println!("Ошибка: {}", e),
}fn print_length(s: &String) {
println!("{}", s.len());
}
let s = String::from("hello");
print_length(&s); // заимствование (borrow)#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(2, 3), 5);
}
}Traits — это интерфейсы в стиле Rust.
trait Greet {
fn greet(&self) -> String;
}
struct Person {
name: String,
}
impl Greet for Person {
fn greet(&self) -> String {
format!("Привет, {}!", self.name)
}
}Модули позволяют организовать код по файлам и папкам.
// src/main.rs
mod utils;
fn main() {
utils::hello();
}
// src/utils.rs
pub fn hello() {
println!("Привет из модуля!");
}mod— объявляет модульpub— делает функцию/структуру доступной снаружиuse— импорт:
use crate::utils::hello;use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() {
say_hello().await;
}
async fn say_hello() {
sleep(Duration::from_secs(1)).await;
println!("Привет, async!");
}Для async-кода часто используется tokio или async-std
В Cargo.toml:
[dependencies]
tokio = { version = "1", features = ["full"] }let numbers = vec![1, 2, 3, 4, 5];
let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();Возвращает следующий элемент или None, если элементы закончились
Преобразует итератор в коллекцию (Vec, HashMap, и т.д.)
Преобразует каждый элемент
let doubled: Vec<i32> = vec![1, 2, 3].iter().map(|x| x * 2).collect();Пропускает элементы, не проходящие условие
let even: Vec<i32> = vec![1,2,3,4].into_iter().filter(|x| x % 2 == 0).collect();Оборачивает элементы в (index, item)
for (i, val) in vec.iter().enumerate() {
println!("{}: {}", i, val);
}Сводит элементы к одному значению
let sum = vec![1,2,3].iter().fold(0, |acc, x| acc + x);Находит первый подходящий элемент
let found = vec![1, 2, 3].iter().find(|&&x| x > 1); // Some(&2)Проверяют все/хотя бы один элемент
vec![1,2,3].iter().all(|x| *x > 0); // true
vec![1,2,3].iter().any(|x| *x == 2); // trueБерёт первые n элементов
let first_two: Vec<_> = vec![1,2,3].into_iter().take(2).collect();Пропускает первые n элементов
let rest: Vec<_> = vec![1,2,3].into_iter().skip(1).collect(); // [2, 3]Объединяет два итератора
let a = [1, 2, 3];
let b = ["a", "b", "c"];
let zipped: Vec<_> = a.iter().zip(b.iter()).collect();Соединяет два итератора
let joined: Vec<_> = [1,2].iter().chain([3,4].iter()).collect();«Разворачивает» вложенные итераторы
let nested = vec![vec![1,2], vec![3,4]];
let flat: Vec<_> = nested.into_iter().flatten().collect(); // [1,2,3,4]Как map + flatten
let nums = vec![1, 2];
let result: Vec<_> = nums.into_iter().flat_map(|x| vec![x, x * 10]).collect(); // [1,10,2,20]let maybe_number: Option<i32> = Some(10);
match maybe_number {
Some(n) => println!("Число: {}", n),
None => println!("Нет значения"),
}fn safe_div(a: i32, b: i32) -> Result<i32, &'static str> {
if b == 0 {
Err("Нельзя делить на ноль")
} else {
Ok(a / b)
}
}let (x, y) = (10, 20);
match (x, y) {
(0, _) => println!("x == 0"),
(_, 0) => println!("y == 0"),
_ => println!("Оба не нули"),
}| Указатель | Назначение | Изменяемость | Поддержка потоков | Когда использовать | Проверка времени |
|---|---|---|---|---|---|
Box<T> |
Выделение в куче | ❌ (без mut) |
✅ (если T: Send) |
Рекурсивные типы, перенос тяжёлых структур | Компиляция |
Rc<T> |
Подсчёт ссылок (однопоточный) | ❌ | 🚫 | Разделение доступа без изменения | Компиляция |
RefCell<T> |
Внутренняя изменяемость | ✅ | 🚫 | Изменение внутри &self в одном потоке |
Выполнение |
Rc<RefCell<T>> |
Разделение + изменяемость (однопоточный) | ✅ | 🚫 | Общий доступ с возможностью изменения | Выполнение |
Arc<T> |
Подсчёт ссылок (многопоточный) | ❌ | ✅ | Разделение между потоками | Компиляция |
Arc<Mutex<T>> |
Разделение + изменяемость (многопоточный) | ✅ | ✅ | Общие данные с безопасным изменением | Блокировка + выполнение |
Cell<T> |
Копируемые типы с изменяемостью | ✅ (Copy) |
🚫 | Изменение примитивов в &self |
Выполнение |
Mutex<T> |
Защита от одновременного доступа | ✅ | ✅ | Синхронизация данных между потоками | Выполнение |
Ref<T> / RefMut<T> |
Возвращаются из RefCell<T> |
✅ / ✅ | 🚫 | Управление временем жизни ссылок | Выполнение |
Weak<T> |
Слабая ссылка на Rc/Arc |
❌ | Зависит от обёртки | Избегать циклических ссылок | Компиляция |
let b = Box::new(5); // перемещает значение 5 в кучу (heap)
println!("b = {}", b); // автоматически разыменовывается — печатает 5📌 Что происходит:
Box::new(5)создаёт указатель на 5 в кучеbвладеет этим значениемBoxосвобождает память при выходе из области видимости
Полезно в рекурсиях:
enum List {
Cons(i32, Box<List>),
Nil,
}Reference Counter (Ref Count) — это механизм, который подсчитывает количество владельцев одного объекта в памяти. Как только последняя ссылка удаляется — объект освобождается автоматически.
Структура Rc примерно такая:
struct RcBox<T> {
strong: Cell<usize>, // счётчик "сильных" ссылок
value: T, // значение
}Пример:
let a = Rc::new(String::from("hello")); // Создаётся `RcBox { strong: 1, value: "hello" }`
let b = Rc::clone(&a); // `clone()` увеличивает `strong` до `2`📦 Под капотом:
- Создаётся
RcBox { strong: 1, value: "hello" } clone()увеличиваетstrongдо2- При
drop(b)→strong -= 1 - Когда
strong == 0, вызываетсяdrop(value)и память освобождается
Ссылка называется "сильной" потому, что сильная ссылка, в отличие от слабой - увеличивает счётчик.
use std::rc::Rc;
let a = Rc::new(String::from("hello")); // создаём строку в куче с подсчётом ссылок
let b = Rc::clone(&a); // увеличиваем счётчик ссылок (оба a и b владеют строкой)
println!("a = {}, b = {}", a, b);
println!("Rc count = {}", Rc::strong_count(&a)); // 2📌 Что происходит:
aсоздаёт объект с 1 владельцемRc::cloneделает поверхностное копирование — счётчик = 2- Строка удалится, когда все владельцы выйдут из области видимости
Полезно при построении графов или деревьев с разделяемыми узлами.
use std::cell::RefCell;
let x = RefCell::new(5); // создаём обёртку с возможностью изменять
*x.borrow_mut() += 1; // получаем мутабельную ссылку и изменяем значение
println!("x = {}", x.borrow()); // borrow() даёт неизменяемую ссылку: 6📌 Что происходит:
borrow_mut()даёт временный доступ на изменение- После этого
borrow()можно получить только если нет активныхborrow_mut() - Проверки происходят во время выполнения, не компиляции
use std::rc::Rc;
use std::cell::RefCell;
let data = Rc::new(RefCell::new(42)); // оборачиваем значение в RefCell внутри Rc
let a = Rc::clone(&data); // увеличиваем счётчик Rc
*a.borrow_mut() += 1; // изменяем внутреннее значение
println!("data = {}", data.borrow()); // 43📌 Что происходит:
Rcуправляет владением,RefCellдаёт возможность временного изменяемого доступа- Можно создать сколько угодно
Rc::clone, но данные внутри — только черезborrow()/borrow_mut() - Полезно в структурах с несколькими владельцами, которые хотят менять содержимое (очень популярен в tree/graph структурах)
use std::sync::Arc;
let shared = Arc::new(5); // создаёт объект в куче с атомарным счётчиком ссылок
let t1 = Arc::clone(&shared); // клонируем владение для другого потока
println!("t1 = {}, shared = {}", t1, shared); // оба видят одно и то же📌 Что происходит:
Arc(atomic Rc) безопасен для использования между потоками- Внутреннее значение должно быть иммутабельным или защищено (
Mutex), если нужно изменять
use std::sync::{Arc, Mutex};
use std::thread;
let counter = Arc::new(Mutex::new(0)); // оборачиваем значение в Mutex и Arc
let c = Arc::clone(&counter); // клонируем владение
let handle = thread::spawn(move || {
let mut num = c.lock().unwrap(); // получаем эксклюзивный доступ
*num += 1; // изменяем значение (0 → 1)
});
handle.join().unwrap();
println!("counter = {:?}", counter.lock().unwrap()); // 1📌 Что происходит:
Mutex::new(0)создаёт значение, защищённое от одновременного доступа.lock()даёт доступ и блокирует другие потокиArcделит владение между потоками- После выхода потока из области видимости,
Mutexразблокируется
use std::cell::Cell;
let c = Cell::new(10); // инициализация Cell с Copy-типом
c.set(20); // перезапись значения
let x = c.get(); // получение текущего значения (20)
println!("x = {}", x);📌 Что происходит:
- Работает только с типами, реализующими
Copy - Можно менять значение даже внутри
&self(например, вimpl)
| Требуется | Используй |
|---|---|
| Хранить в куче | Box<T> |
| Разделить между владельцами | Rc<T> или Arc<T> |
| Внутренне изменять | RefCell<T> или Mutex<T> |
| Потокобезопасность | Arc<T> + Mutex<T> |
macro_rules! say_hello {
() => {
println!("Привет из макроса!");
};
}
fn main() {
say_hello!();
}fn longest<'a>(a: &'a str, b: &'a str) -> &'a str {
if a.len() > b.len() { a } else { b }
}'a— параметр времени жизни&'a str— строковая ссылка, живущая не меньше'a- Возвращаемая ссылка также должна жить хотя бы
'a
🧠 Это значит: результат живёт столько же, сколько самый короткий из входов.
static NAME: &str = "Rustacean"; // живёт всю программу
fn get_name() -> &'static str {
"Forever" // строковый литерал живёт в статической памяти
}fn combine<'a>(a: &'a str, b: &'a str) -> String {
format!("{}{}", a, b)
}Структура не может жить дольше, чем её ссылки.
struct Book<'a> {
title: &'a str,
author: &'a str,
}Основные Smart Pointer’ы в Rust Указатель Назначение Смена владельца Многопоточность Box Куча. Хранит один объект ✅ move 🚫 Rc Подсчёт ссылок (однопоточный) ✅ clone() 🚫 Arc Подсчёт ссылок (многопоточный) ✅ clone() ✅ RefCell Внутренняя изменяемость (однопоточная) ✅ через borrow() 🚫 Mutex Потокобезопасная внутренняя изменяемость ✅ через lock() ✅