Умные указатели и что дальше

Обзорно знакомимся с умными указателями Box и Rc и намечаем путь дальнейшего изучения Rust.

Умный указатель — тип, который ведёт себя как ссылка, но несёт дополнительные возможности владения и управления памятью.

Box — значение в куче

Обычно Rust размещает значения на стеке. Box<T> кладёт значение в кучу, оставляя на стеке лишь указатель. Это нужно, когда размер неизвестен на этапе компиляции — например, для рекурсивных структур данных, которые иначе были бы «бесконечного» размера.

// рекурсивный список: каждый узел владеет следующим через Box
enum List {
    Cons(i32, Box<List>), // Box даёт известный размер указателя
    Nil,
}

use List::{Cons, Nil};

fn main() {
    let list = Cons(1, Box::new(Cons(2, Box::new(Nil))));
    // обходим список
    let mut node = &list;
    while let Cons(value, next) = node {
        println!("{value}");
        node = next;
    }
}

Вывод:

1
2

Без Box компилятор не смог бы вычислить размер List: он содержал бы сам себя бесконечно. Box разрывает рекурсию, ведь размер указателя известен и фиксирован.

Rc — несколько владельцев

Правило «один владелец» иногда мешает: бывает, что одни данные должны принадлежать нескольким местам сразу, например узел графа, на который ссылаются разные узлы. Для этого есть Rc<T> (reference counted) — он ведёт счётчик владельцев и освобождает данные, когда последний владелец исчезает.

use std::rc::Rc;

fn main() {
    let a = Rc::new(String::from("общие данные"));
    let b = Rc::clone(&a); // не копия данных, а ещё один владелец (+1 к счётчику)
    let c = Rc::clone(&a);

    println!("{}", a);
    println!("владельцев: {}", Rc::strong_count(&a)); // 3
    let _ = (b, c);
}

Вывод:

общие данные
владельцев: 3

Rc::clone не копирует сами данные — он лишь увеличивает счётчик ссылок. Это дёшево. Rc работает в однопоточном коде; для многопоточного есть его потокобезопасный аналог Arc.

УказательЗачем
Box<T>значение в куче, рекурсивные типы, один владелец
Rc<T>несколько владельцев в одном потоке (счётчик ссылок)
Arc<T>несколько владельцев в многопоточном коде

Что изучать дальше

Вы освоили фундамент Rust. Следующие большие темы:

  • Конкурентность. Потоки, каналы, Arc и Mutex. Главное обещание Rust здесь — «бесстрашная многопоточность»: borrow checker не пропускает гонки данных и в конкурентном коде.
  • Асинхронность. async/await и рантаймы вроде Tokio для тысяч одновременных задач — основа сетевых сервисов.
  • Обработка ошибок в реальных проектах. Крейты thiserror и anyhow упрощают работу с Result.
  • Трейт-объекты и динамическая диспетчеризация (dyn Trait) для гетерогенных коллекций.

Главное вы уже умеете: думать в терминах владения и заимствования. Когда компилятор ругается, спрашивайте себя «кто владеет этими данными и как долго живёт ссылка» — и ответ почти всегда найдётся.

Итог

  • Box<T> размещает значение в куче; нужен для рекурсивных типов.
  • Rc<T> даёт нескольких владельцев через счётчик ссылок (Arc — для потоков).
  • Дальше: конкурентность, async/await, трейт-объекты — но фундамент владения у вас уже есть.
Проверьте себя
1. Зачем нужен Box<T>?
AЧтобы копировать данные
BЧтобы разместить значение в куче, например для рекурсивных типов известного размера
CЧтобы ускорить стек
DЧтобы запретить владение
2. Что делает Rc::clone?
AДелает глубокую копию данных
BУвеличивает счётчик ссылок, добавляя ещё одного владельца тех же данных
CУдаляет данные
DПеремещает владение
3. Какой умный указатель выбрать для нескольких владельцев в многопоточном коде?
ABox
BRc
CArc
D&mut
Поддержать проект