# Inteligentne wskaźniki w Rust: Box, Rc, Arc i RefCell w 2026 > Inteligentne wskaźniki Rust Box, Rc, Arc i RefCell wyjaśnione na kompilowalnych przykładach z 2026, z tabelą decyzyjną i pytaniami rekrutacyjnymi. - Published: 2026-07-05 - Updated: 2026-07-07 - Author: SharpSkill - Tags: rust, smart-pointers, memory-management, interview, rust-2024-edition - Reading time: 11 min --- Inteligentne wskaźniki w Rust to narzędzia, które odblokowują wzorce własności, jakich sam borrow checker nie potrafi wyrazić: alokację na stercie, współdzieloną własność oraz mutację za współdzieloną referencją. Podczas gdy zwykła referencja (`&T`) jedynie pożycza wartość, inteligentny wskaźnik jest właścicielem swoich danych i nakłada na nie dodatkowe zachowanie. Ten przewodnik omawia cztery typy, które każdy programista Rusta spotyka w produkcji i na rozmowach kwalifikacyjnych: `Box`, `Rc`, `Arc` i `RefCell`, wraz z kompilowalnymi przykładami dla edycji Rust 2024. > **Podsumowanie w jednym zdaniu** > > Używaj `Box` do alokacji na stercie z jednym właścicielem, `Rc` do współdzielonej własności w obrębie jednego wątku, `Arc` do współdzielonej własności między wątkami oraz `RefCell` do mutacji wartości przez współdzieloną referencję. Pary `Rc>` i `Arc>` pokrywają współdzielony, modyfikowalny stan. ## Czym są inteligentne wskaźniki w Rust Inteligentny wskaźnik to struktura, która zachowuje się jak wskaźnik, ale niesie dodatkowe metadane lub możliwości. Większość implementuje cechę `Deref`, dzięki czemu `*pointer` oraz wywołania metod działają tak, jakby wskaźnik był zwykłą referencją, a także cechę `Drop`, dzięki czemu sprzątanie następuje automatycznie, gdy wartość opuszcza zasięg. Biblioteka standardowa dostarcza cztery typy omawiane tutaj, a ich zrozumienie zależy od solidnego opanowania [własności i pożyczania](/blog/rust/ownership-borrowing-rust-complete-guide), które decyduje o tym, kto i kiedy zwalnia każdą alokację. Kluczową różnicą względem referencji jest własność. `&T` nigdy nie jest właścicielem danych, na które wskazuje, więc nie może przeżyć wartości. Inteligentny wskaźnik jest właścicielem danych, kontroluje ich czas życia i zwalnia je deterministycznie. [Rozdział książki Rust o inteligentnych wskaźnikach](https://doc.rust-lang.org/book/ch15-00-smart-pointers.html) traktuje je jako kategorię właśnie dlatego, że łączy je ten sam kształt: własność plus zachowanie. ## `Box`: alokacja na stercie dla typów rekurencyjnych i o znanym rozmiarze `Box` to najprostszy inteligentny wskaźnik. Przechowuje wartość na stercie, a na stosie trzyma wskaźnik do niej, z jednym właścicielem i bez narzutu w czasie wykonania poza samą alokacją. Jego najczęstszym zadaniem jest nadanie typom rekurencyjnym znanego rozmiaru: typ, który zawiera samego siebie bezpośrednio, byłby nieskończenie duży, ale `Box` to tylko wskaźnik, więc jego rozmiar jest stały niezależnie od tego, na co wskazuje. Poniższy przykład definiuje drzewo binarne. Każdy `Node` przechowuje dwoje dzieci i bez pośredniości kompilator nie potrafi obliczyć rozmiaru `Tree`. Opakowanie każdego dziecka w `Box` przerywa rekurencję na poziomie typu. ```rust // tree.rs #[derive(Debug)] enum Tree { Leaf(i32), // Box puts each child on the heap, so Node has a fixed size (two pointers). Node(Box, Box), } fn sum(tree: &Tree) -> i32 { match tree { Tree::Leaf(value) => *value, // base case: return the leaf Tree::Node(left, right) => sum(left) + sum(right), // recurse into both children } } fn main() { // Build (1) + ((2) + (3)) = 6 let tree = Tree::Node( Box::new(Tree::Leaf(1)), Box::new(Tree::Node( Box::new(Tree::Leaf(2)), Box::new(Tree::Leaf(3)), )), ); println!("sum = {}", sum(&tree)); // sum = 6 } ``` `Box` ma też znaczenie, gdy przenoszenie dużej wartości byłoby kosztowne, albo gdy zwracany jest obiekt cechy, taki jak `Box`. W każdym przypadku zasada jest ta sama: jeden właściciel, zwalniany automatycznie, gdy `Box` zostaje usunięty. ## `Rc`: współdzielona własność w kodzie jednowątkowym Czasem wartość potrzebuje kilku właścicieli i żaden z nich nie jest w oczywisty sposób ostatnim, który jej używa. `Rc` (liczone referencje) rozwiązuje to, utrzymując silny licznik istniejących właścicieli. `Rc::clone` zwiększa ten licznik bez kopiowania danych bazowych, a każde usunięcie go zmniejsza. Gdy licznik osiąga zero, wartość zostaje zwolniona. Przykładem jest obiekt konfiguracji odczytywany przez kilku pracowników. Każdy pracownik powinien trzymać konfigurację tak długo, jak jej potrzebuje, a alokacja powinna zniknąć dopiero wtedy, gdy zniknie ostatni pracownik. ```rust // shared_config.rs use std::rc::Rc; #[derive(Debug)] struct Config { endpoint: String, timeout_ms: u32, } fn main() { // Rc::new moves Config onto the heap with a strong count of 1. let config = Rc::new(Config { endpoint: "https://api.example.com".to_string(), timeout_ms: 5000, }); // Rc::clone only bumps the reference count; it does not deep-copy Config. let worker_a = Rc::clone(&config); let worker_b = Rc::clone(&config); // All three handles point to the same allocation. println!("endpoint: {}", worker_a.endpoint); println!("timeout: {}", worker_b.timeout_ms); // strong_count reports how many owners currently hold the value. println!("owners: {}", Rc::strong_count(&config)); // owners: 3 } ``` Jawne wywołanie `Rc::clone` (zamiast `config.clone()`) jest idiomatyczne: sygnalizuje czytelnikom, że operacja jest tania i dotyczy jedynie licznika. Haczyk polega na tym, że `Rc` udostępnia wyłącznie współdzielone, niemutowalne referencje. Nie może mutować współdzielonej wartości i nie jest bezpieczny do przesyłania między wątkami. [Dokumentacja `Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html) wyjaśnia oba ograniczenia. ## `RefCell` i wewnętrzna mutowalność w Rust Rust zwykle wymusza, aby wartość była albo współdzielona przez wiele niemutowalnych referencji, albo mutowana przez dokładnie jedną mutowalną referencję, i sprawdza to w czasie kompilacji. `RefCell` zapewnia wewnętrzną mutowalność: pozwala kodowi mutować wartość przez współdzieloną referencję, przenosząc to samo sprawdzenie na czas wykonania. `borrow()` zwraca współdzielony uchwyt do odczytu; `borrow_mut()` zwraca wyłączny uchwyt do zapisu. Reguły są identyczne, ale ich naruszenia powodują panikę zamiast błędu kompilacji. W połączeniu z `Rc` daje to kanoniczny jednowątkowy wzorzec współdzielonego, mutowalnego stanu `Rc>`: kilku właścicieli, którzy wszyscy mogą aktualizować tę samą wartość. ```rust // counter.rs use std::rc::Rc; use std::cell::RefCell; // Rc gives shared ownership; RefCell allows mutation through a shared reference. type SharedCounter = Rc>; fn increment(counter: &SharedCounter) { // borrow_mut() hands out an exclusive reference, checked at runtime. *counter.borrow_mut() += 1; } fn main() { let counter: SharedCounter = Rc::new(RefCell::new(0)); let handle_a = Rc::clone(&counter); let handle_b = Rc::clone(&counter); increment(&handle_a); increment(&handle_b); increment(&counter); // borrow() gives a shared read guard; the value is now 3. println!("count = {}", counter.borrow()); // count = 3 } ``` > **RefCell przenosi sprawdzanie pożyczeń na czas wykonania** > > Wywołanie `borrow_mut()`, gdy inny uchwyt `borrow()` lub `borrow_mut()` wciąż żyje, powoduje panikę z komunikatem `already borrowed: BorrowMutError`. Gwarancje bezpieczeństwa nadal obowiązują, ale błąd logiczny, który kompilator by wychwycił, staje się awarią w czasie wykonania. Uchwyty należy trzymać krótko i unikać przetrzymywania pożyczki przez wywołanie funkcji, które mogłoby ponownie wejść w ten sam `RefCell`. Wewnętrzna mutowalność to również miejsce, gdzie kryją się cykle referencji. Dwie wartości `Rc>`, które wskazują na siebie nawzajem, na zawsze utrzymują swój silny licznik powyżej zera, więc żadna nigdy nie zostaje zwolniona. Rozwiązaniem jest `Weak`, uchwyt bez własności, który nie wpływa na silny licznik; klasyczne omówienie znajduje się w [Learn Rust With Entirely Too Many Linked Lists](https://rust-unofficial.github.io/too-many-lists/). ## `Arc`: bezpieczne wątkowo liczenie referencji dla współbieżności `Arc` (atomowo liczone referencje) to wielowątkowy odpowiednik `Rc`. Publiczne API jest niemal identyczne, ale licznik używa operacji atomowych, dzięki czemu klonowanie i usuwanie `Arc` z kilku wątków naraz pozostaje poprawne. Ta atomowość jest powodem, dla którego `Arc` wybiera się tylko wtedy, gdy współdzielenie przekracza granicę wątku: atomowe zwiększanie licznika jest mierzalnie wolniejsze od zwykłego, którego używa `Rc`. Ponieważ `Arc` nadal udostępnia niemutowalne referencje, mutacja między wątkami wymaga prymitywu synchronizacji. `Mutex` to zwykły partner, dający wzorzec `Arc>`, który odzwierciedla `Rc>` dla kodu współbieżnego. Przykład uruchamia cztery wątki, z których każdy tysiąc razy zwiększa współdzieloną sumę. ```rust // parallel_sum.rs use std::sync::{Arc, Mutex}; use std::thread; fn main() { // Arc is safe to share across threads; Mutex serializes access to the inner value. let total = Arc::new(Mutex::new(0u64)); let mut handles = Vec::new(); for _ in 0..4 { // Each thread gets its own Arc handle (an atomic count bump). let total = Arc::clone(&total); handles.push(thread::spawn(move || { for _ in 0..1000 { // lock() blocks until the mutex is free, then returns a guard. let mut value = total.lock().unwrap(); *value += 1; } })); } for handle in handles { handle.join().unwrap(); // wait for every thread before reading } println!("total = {}", *total.lock().unwrap()); // total = 4000 } ``` Kompilator wymusza tę granicę: `Rc` nie jest `Send`, więc próba przeniesienia go do `thread::spawn` nie skompiluje się, co kieruje ku `Arc`. Sięganie po `Arc` w obrębie jednego wątku to jedynie płacenie za atomy, których nigdy się nie używa. [Dokumentacja `Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html) szczegółowo omawia gwarancje uporządkowania pamięci, a `Arc>` stanowi podstawę większości współdzielonego stanu w [asynchronicznym Rust z Tokio](/blog/rust/rust-async-await-tokio-futures-concurrency). ## Box vs Rc vs Arc vs RefCell: kiedy używać którego Te cztery typy komponują się wzdłuż dwóch osi: ilu właścicieli ma wartość oraz czy można ją mutować przez współdzielony uchwyt. Tabela podsumowuje kompromisy. | Typ | Własność | Mutacja przez współdzielony uchwyt | Bezpieczny wątkowo | Narzut | |------|-----------|----------------------------|-------------|----------| | `Box` | Pojedyncza | Nie | Tak, jeśli `T: Send` | Tylko alokacja na stercie | | `Rc` | Współdzielona | Nie | Nie | Nieatomowy licznik | | `Arc` | Współdzielona | Nie | Tak | Atomowy licznik | | `RefCell` | Pojedyncza | Tak (sprawdzana w czasie wykonania) | Nie | Flaga pożyczenia w czasie wykonania | Praktyczne drzewo decyzyjne jest krótkie. Potrzebny jeden właściciel na stercie: `Box`. Potrzebnych wielu właścicieli w jednym wątku: `Rc`. Potrzebnych wielu właścicieli między wątkami: `Arc`. Trzeba mutować współdzieloną wartość: należy opakować typ wewnętrzny w `RefCell` (jeden wątek) lub `Mutex` (wiele wątków). Wskazówki z Effective Rust dotyczące [typów referencji i wskaźników](https://www.lurklurk.org/effective-rust/) prowadzą do tej samej warstwowości. Najlepiej zacząć od najmniej rozbudowanej opcji i dodawać możliwości dopiero wtedy, gdy wymusi to kompilator. ## Pytania rekrutacyjne o inteligentne wskaźniki w Rust Inteligentne wskaźniki to niezawodny temat na rozmowach kwalifikacyjnych, ponieważ sprawdzają, czy kandydat rozumie własność, a nie tylko składnię. Poniższe pytania pojawiają się często, a więcej z nich zebrano w [module rekrutacyjnym o inteligentnych wskaźnikach w Rust](/technologies/rust/interview-questions/smart-pointers). **Jaka jest różnica między `Rc` a `Arc`?** Oba zapewniają współdzieloną własność przez liczenie referencji. `Rc` używa nieatomowego licznika i jest ograniczony do jednego wątku; `Arc` używa operacji atomowych i może być współdzielony między wątkami przy niewielkim koszcie w czasie wykonania. Kompilator wymusza ten podział: `Rc` nie jest ani `Send`, ani `Sync`, więc nie może przekroczyć granicy wątku. **Dlaczego łączy się `Rc` z `RefCell`?** `Rc` daje współdzieloną własność, ale wyłącznie niemutowalny dostęp. `RefCell` dodaje wewnętrzną mutowalność, pozwalając właścicielom mutować wartość przez współdzieloną referencję ze sprawdzaniem pożyczeń w czasie wykonania. Razem `Rc>` stanowi idiomatyczny jednowątkowy budulec współdzielonego, mutowalnego stanu. **Jak liczenie referencji może powodować wyciek pamięci w Rust?** Dwie wartości `Rc` (lub `Arc`), które odwołują się do siebie nawzajem, tworzą cykl, którego silne liczniki nigdy nie osiągają zera, więc alokacja nigdy nie zostaje zwolniona. Przerwanie cyklu za pomocą `Weak`, który trzyma referencję bez własności, przywraca poprawne sprzątanie. **Kiedy `Box` jest lepszy od `Rc`?** Zawsze, gdy wystarcza jeden właściciel. `Box` nie ma narzutu liczenia referencji, więc jest domyślnym wyborem do alokacji na stercie, typów rekurencyjnych i obiektów cech. Po `Rc` warto sięgać tylko wtedy, gdy potrzebna jest rzeczywista współdzielona własność. ## Podsumowanie Inteligentne wskaźniki w Rust zamieniają własność z ograniczenia w zestaw komponowalnych klocków. Wybór spośród nich wynika wprost z kształtu problemu: - Warto sięgnąć po `Box`, gdy wartość potrzebuje jednego właściciela na stercie, typ rekurencyjny potrzebuje stałego rozmiaru albo funkcja zwraca obiekt cechy. - `Rc` sprawdza się przy wielu właścicielach w jednym wątku, a przełączenie na `Arc` następuje w momencie, gdy własność przekracza granicę wątku. - `RefCell` dodaje wewnętrzną mutowalność w kodzie jednowątkowym, a `Mutex` to jego współbieżny odpowiednik; uchwyty pożyczenia i blokady należy trzymać krótko. - Łączenie ich powinno być świadome: `Rc>` dla współdzielonego, mutowalnego stanu w jednym wątku, `Arc>` między wątkami. - Trzeba uważać na cykle referencji między liczonymi wskaźnikami i przerywać je za pomocą `Weak`, aby uniknąć wycieków. - Domyślnie warto wybierać najmniej rozbudowany typ i pozwolić kompilatorowi wskazać, kiedy potrzebna jest większa moc. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pl/blog/rust/rust-smart-pointers-box-rc-arc-refcell